@@ -2,7 +2,7 @@ package tscfg
2
2
3
3
import com .typesafe .config ._
4
4
import tscfg .generators .tsConfigUtil
5
- import tscfg .model .{DURATION , SIZE }
5
+ import tscfg .model .{AnnType , DURATION , ObjectType , SIZE }
6
6
import tscfg .model .durations .ms
7
7
8
8
import scala .collection .JavaConverters ._
@@ -35,37 +35,53 @@ class ModelBuilder(assumeAllRequired: Boolean = false) {
35
35
36
36
def build (conf : Config ): ModelBuildResult = {
37
37
warns.clear()
38
- ModelBuildResult (objectType = fromConfig(conf),
38
+ ModelBuildResult (objectType = fromConfig(Namespace .root, conf),
39
39
warnings = warns.toList.sortBy(_.line))
40
40
}
41
41
42
42
private val warns = collection.mutable.ArrayBuffer [Warning ]()
43
43
44
- private def fromConfig (conf : Config ): model.ObjectType = {
44
+ private def fromConfig (namespace : Namespace , conf : Config ): model.ObjectType = {
45
+ // do two passes as lightbend config does not necessarily preserve member order:
46
+ val ot = fromConfig1(namespace, conf)
47
+ fromConfig2(namespace, ot)
48
+ }
49
+
50
+ private def fromConfig1 (namespace : Namespace , conf : Config ): model.ObjectType = {
45
51
val memberStructs = getMemberStructs(conf)
46
52
val members : immutable.Map [String , model.AnnType ] = memberStructs.map { childStruct ⇒
47
53
val name = childStruct.name
48
54
val cv = conf.getValue(name)
49
55
50
56
val (childType, optional, default) = {
51
57
if (childStruct.isLeaf) {
52
- val typ = fromConfigValue(cv)
53
58
val valueString = util.escapeValue(cv.unwrapped().toString)
54
- if (typ == model.STRING ) {
55
- toAnnBasicType(valueString) match {
56
- case Some (annBasicType) ⇒
57
- annBasicType
58
- case None ⇒
59
- (typ, true , Some (valueString))
60
- }
59
+
60
+ getTypeFromConfigValue(namespace, cv, valueString) match {
61
+ case typ : model.STRING .type ⇒
62
+ namespace.resolveDefine(valueString) match {
63
+ case Some (ort) ⇒
64
+ (ort, false , None )
65
+
66
+ case None ⇒
67
+ toAnnBasicType(valueString) match {
68
+ case Some (annBasicType) ⇒
69
+ annBasicType
70
+
71
+ case None ⇒
72
+ (typ, true , Some (valueString))
73
+ }
74
+ }
75
+
76
+ case typ : model.BasicType ⇒
77
+ (typ, true , Some (valueString))
78
+
79
+ case typ ⇒
80
+ (typ, false , None )
61
81
}
62
- else if (typ.isInstanceOf [model.BasicType ])
63
- (typ, true , Some (valueString))
64
- else
65
- (typ, false , None )
66
82
}
67
83
else {
68
- (fromConfig(conf.getConfig(name)), false , None )
84
+ (fromConfig(namespace.extend(name), conf.getConfig(name)), false , None )
69
85
}
70
86
}
71
87
@@ -89,15 +105,51 @@ class ModelBuilder(assumeAllRequired: Boolean = false) {
89
105
// s"assumeAllRequired=$assumeAllRequired optFromComments=$optFromComments " +
90
106
// s"adjName=$adjName")
91
107
92
- adjName -> model.AnnType (childType,
108
+ val annType = model.AnnType (childType,
93
109
optional = effOptional,
94
110
default = effDefault,
95
111
comments = commentsOpt
96
112
)
113
+
114
+ if (annType.isDefine) {
115
+ namespace.addDefine(name, childType)
116
+ }
117
+
118
+ adjName -> annType
119
+
97
120
}.toMap
98
121
model.ObjectType (members)
99
122
}
100
123
124
+ private def fromConfig2 (namespace : Namespace , ot : model.ObjectType ): model.ObjectType = {
125
+ val resolvedMembers = ot.members.map { case (name, annType) ⇒
126
+ val modAnnType = annType.t match {
127
+
128
+ case _:model.STRING .type ⇒
129
+ annType.default match {
130
+ case Some (strValue) ⇒
131
+ namespace.resolveDefine(strValue) match {
132
+ case Some (ort) ⇒ AnnType (ort)
133
+ case _ ⇒ annType
134
+ }
135
+
136
+ case None ⇒ annType
137
+ }
138
+
139
+ // // the following would be part of changes to allow recursive type
140
+ // case ot:ObjectType ⇒
141
+ // val ot2 = fromConfig2(namespace, ot)
142
+ // AnnType(ot2)
143
+
144
+ case _ ⇒ annType
145
+ }
146
+
147
+ name → modAnnType
148
+ }
149
+
150
+ model.ObjectType (resolvedMembers)
151
+ }
152
+
101
153
private case class Struct (name : String , members : mutable.HashMap [String , Struct ] = mutable.HashMap .empty) {
102
154
def isLeaf : Boolean = members.isEmpty
103
155
// $COVERAGE-OFF$
@@ -151,14 +203,14 @@ class ModelBuilder(assumeAllRequired: Boolean = false) {
151
203
structs(" " ).members.values.toList
152
204
}
153
205
154
- private def fromConfigValue ( cv : ConfigValue ): model.Type = {
206
+ private def getTypeFromConfigValue ( namespace : Namespace , cv : ConfigValue , valueString : String ): model.Type = {
155
207
import ConfigValueType ._
156
208
cv.valueType() match {
157
209
case STRING => model.STRING
158
210
case BOOLEAN => model.BOOLEAN
159
211
case NUMBER => numberType(cv.unwrapped().toString)
160
- case LIST => listType(cv.asInstanceOf [ConfigList ])
161
- case OBJECT => objType(cv.asInstanceOf [ConfigObject ])
212
+ case LIST => listType(namespace, cv.asInstanceOf [ConfigList ])
213
+ case OBJECT => objType(namespace, cv.asInstanceOf [ConfigObject ])
162
214
case NULL => throw new AssertionError (" null unexpected" )
163
215
}
164
216
}
@@ -200,7 +252,7 @@ class ModelBuilder(assumeAllRequired: Boolean = false) {
200
252
}
201
253
}
202
254
203
- private def listType (cv : ConfigList ): model.ListType = {
255
+ private def listType (namespace : Namespace , cv : ConfigList ): model.ListType = {
204
256
if (cv.isEmpty) throw new IllegalArgumentException (" list with one element expected" )
205
257
206
258
if (cv.size() > 1 ) {
@@ -211,24 +263,31 @@ class ModelBuilder(assumeAllRequired: Boolean = false) {
211
263
}
212
264
213
265
val cv0 : ConfigValue = cv.get(0 )
214
- val typ = fromConfigValue(cv0)
266
+ val valueString = util.escapeValue(cv0.unwrapped().toString)
267
+ val typ = getTypeFromConfigValue(namespace, cv0, valueString)
215
268
216
269
val elemType = {
217
- val valueString = util.escapeValue(cv0.unwrapped().toString)
218
270
if (typ == model.STRING ) {
219
- // see possible type from the string literal:
220
- toAnnBasicType(valueString) match {
221
- case Some ((basicType, isOpt, defaultValue)) ⇒
222
- if (isOpt)
223
- warns += OptListElemWarning (cv0.origin().lineNumber(), valueString)
224
-
225
- if (defaultValue.isDefined)
226
- warns += DefaultListElemWarning (cv0.origin().lineNumber(), defaultValue.get, valueString)
227
271
228
- basicType
272
+ namespace.resolveDefine(valueString) match {
273
+ case Some (ort) ⇒
274
+ ort
229
275
230
276
case None ⇒
231
- typ
277
+ // see possible type from the string literal:
278
+ toAnnBasicType(valueString) match {
279
+ case Some ((basicType, isOpt, defaultValue)) ⇒
280
+ if (isOpt)
281
+ warns += OptListElemWarning (cv0.origin().lineNumber(), valueString)
282
+
283
+ if (defaultValue.isDefined)
284
+ warns += DefaultListElemWarning (cv0.origin().lineNumber(), defaultValue.get, valueString)
285
+
286
+ basicType
287
+
288
+ case None ⇒
289
+ typ
290
+ }
232
291
}
233
292
}
234
293
else typ
@@ -237,7 +296,8 @@ class ModelBuilder(assumeAllRequired: Boolean = false) {
237
296
model.ListType (elemType)
238
297
}
239
298
240
- private def objType (cv : ConfigObject ): model.ObjectType = fromConfig(cv.toConfig)
299
+ private def objType (namespace : Namespace , cv : ConfigObject ): model.ObjectType =
300
+ fromConfig(namespace, cv.toConfig)
241
301
242
302
private def numberType (valueString : String ): model.BasicType = {
243
303
try {
@@ -279,7 +339,7 @@ object ModelBuilder {
279
339
val filename = args(0 )
280
340
val file = new File (filename)
281
341
val source = io.Source .fromFile(file).mkString.trim
282
- println(" source:\n |" + source.replaceAll(" \n " , " \n |" ))
342
+ // println("source:\n |" + source.replaceAll("\n", "\n |"))
283
343
val result = ModelBuilder (source)
284
344
println(" objectType:" )
285
345
println(model.util.format(result.objectType))
0 commit comments