-
Notifications
You must be signed in to change notification settings - Fork 20
/
value.go
483 lines (403 loc) · 13.6 KB
/
value.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
package tosca_v2_0
import (
"fmt"
"github.com/tliron/kutil/ard"
"github.com/tliron/puccini/tosca"
"github.com/tliron/puccini/tosca/normal"
"github.com/tliron/yamlkeys"
)
//
// Value
//
// [TOSCA-v2.0] @ ?
// [TOSCA-Simple-Profile-YAML-v1.3] @ 3.6.11, 3.6.13
// [TOSCA-Simple-Profile-YAML-v1.2] @ 3.6.10, 3.6.12
// [TOSCA-Simple-Profile-YAML-v1.1] @ 3.5.9, 3.5.11
// [TOSCA-Simple-Profile-YAML-v1.0] @ 3.5.9, 3.5.11
//
type Value struct {
*Entity `name:"value"`
Name string
ConstraintClauses ConstraintClauses
Description *string
DataType *DataType `traverse:"ignore" json:"-" yaml:"-"`
Information *normal.Information `traverse:"ignore" json:"-" yaml:"-"`
rendered bool
}
func NewValue(context *tosca.Context) *Value {
return &Value{
Entity: NewEntity(context),
Name: context.Name,
Information: normal.NewInformation(),
}
}
// tosca.Reader signature
func ReadValue(context *tosca.Context) tosca.EntityPtr {
ToFunctionCall(context)
return NewValue(context)
}
// tosca.Reader signature
// [TOSCA-Simple-Profile-YAML-v1.2] @ 3.6.12.2.2
// [TOSCA-Simple-Profile-YAML-v1.1] @ 3.5.11.2.2
func ReadAttributeValue(context *tosca.Context) tosca.EntityPtr {
self := NewValue(context)
// Unpack long notation
if context.Is(ard.TypeMap) {
map_ := context.Data.(ard.Map)
if len(map_) == 2 {
if description, ok := map_["description"]; ok {
if value, ok := map_["value"]; ok {
self.Description = context.FieldChild("description", description).ReadString()
context.Data = value
}
}
}
}
ToFunctionCall(context)
return self
}
// tosca.Mappable interface
func (self *Value) GetKey() string {
return self.Name
}
// fmt.Stringer interface
func (self *Value) String() string {
return yamlkeys.KeyString(self.Context.Data)
}
func (self *Value) RenderDataType(dataTypeName string) {
if e, ok := self.Context.Namespace.Lookup(dataTypeName); ok {
if dataType, ok := e.(*DataType); ok {
self.RenderAttribute(dataType, nil, false, false)
} else {
self.Context.ReportUnknownDataType(dataTypeName)
}
} else {
self.Context.ReportUnknownDataType(dataTypeName)
}
}
func (self *Value) RenderAttribute(dataType *DataType, definition *AttributeDefinition, bare bool, allowNil bool) {
if self.rendered {
// Avoid rendering more than once (can happen if we were copied from PropertyDefinition.Default)
return
}
self.rendered = true
self.DataType = dataType
if definition != nil {
definition.Render()
}
if !bare {
if self.Description != nil {
self.Information.Description = *self.Description
}
if definition != nil {
self.Information.Definition = definition.GetTypeInformation()
}
if dataType != nil {
self.Information.Type = dataType.GetTypeInformation()
}
}
dataType.Complete(self.Context)
if !bare {
self.ConstraintClauses.Render(dataType)
dataType.ConstraintClauses.Render(dataType)
self.ConstraintClauses = dataType.ConstraintClauses.Append(self.ConstraintClauses)
}
if _, ok := self.Context.Data.(*tosca.FunctionCall); ok {
return
}
if allowNil && (self.Context.Data == nil) {
return
}
// Internal types
if internalTypeName, typeValidator, reader, ok := dataType.GetInternal(); ok {
if typeValidator != nil {
if self.Context.Data == nil {
// Nil data only happens when an attribute is added despite not having a
// "default" value; we will give it a valid zero value instead
if self.Context.Data, ok = ScalarUnitTypeZeroes[internalTypeName]; !ok {
if self.Context.Data, ok = ard.TypeZeroes[internalTypeName]; !ok {
panic(fmt.Sprintf("unsupported internal type name: %s", internalTypeName))
}
}
}
if (internalTypeName == ard.TypeString) && self.Context.HasQuirk(tosca.QuirkDataTypesStringPermissive) {
self.Context.Data = ard.ValueToString(self.Context.Data)
}
// Primitive types
if typeValidator(self.Context.Data) {
// Render list and map elements according to entry schema
// (The entry schema may also have additional constraints)
switch internalTypeName {
case ard.TypeList, ard.TypeMap:
if (definition == nil) || (definition.EntrySchema == nil) || (definition.EntrySchema.DataType == nil) {
// This problem is reported in AttributeDefinition.Render
return
}
if internalTypeName == ard.TypeList {
// Information
entryDataType := definition.EntrySchema.DataType
entryConstraints := definition.EntrySchema.GetConstraints()
self.Information.Entry = entryDataType.GetTypeInformation()
if definition.EntrySchema.Description != nil {
self.Information.Entry.SchemaDescription = *definition.EntrySchema.Description
}
slice := self.Context.Data.(ard.List)
valueList := NewValueList(definition, len(slice), entryConstraints)
for index, data := range slice {
value := ReadAndRenderBareAttribute(self.Context.ListChild(index, data), entryDataType)
valueList.Set(index, value)
}
self.Context.Data = valueList
} else { // ard.TypeMap
if definition.KeySchema == nil {
// This problem is reported in AttributeDefinition.Complete
return
}
// Information
keyDataType := definition.KeySchema.DataType
keyConstraints := definition.KeySchema.GetConstraints()
self.Information.Key = keyDataType.GetTypeInformation()
if definition.KeySchema.Description != nil {
self.Information.Key.SchemaDescription = *definition.KeySchema.Description
}
valueDataType := definition.EntrySchema.DataType
valueConstraints := definition.EntrySchema.GetConstraints()
self.Information.Value = valueDataType.GetTypeInformation()
if definition.EntrySchema.Description != nil {
self.Information.Value.SchemaDescription = *definition.EntrySchema.Description
}
valueMap := NewValueMap(definition, keyConstraints, valueConstraints)
for key, data := range self.Context.Data.(ard.Map) {
// Complex keys are stringified for the purpose of the contexts
// Validate key schema
keyContext := self.Context.MapChild(key, yamlkeys.KeyData(key))
key = ReadAndRenderBareAttribute(keyContext, keyDataType)
context := self.Context.MapChild(key, data)
value := ReadAndRenderBareAttribute(context, valueDataType)
value.ConstraintClauses = ConstraintClauses{}
valueMap.Put(key, value)
}
self.Context.Data = valueMap
}
}
} else {
self.Context.ReportValueWrongType(internalTypeName)
}
} else {
// Special types
self.Context.Data = reader(self.Context)
}
if comparer, ok := dataType.GetMetadataValue("puccini.comparer"); ok {
if hasComparer, ok := self.Context.Data.(HasComparer); ok {
hasComparer.SetComparer(comparer)
} else {
panic(fmt.Sprintf("type has \"puccini.comparer\" metadata but does not support HasComparer interface: %T", self.Context.Data))
}
}
} else if self.Context.ValidateType(ard.TypeMap) {
// Complex data types
map_ := self.Context.Data.(ard.Map)
// All properties must be defined in type
for key := range map_ {
name := yamlkeys.KeyString(key)
if _, ok := dataType.PropertyDefinitions[name]; !ok {
self.Context.MapChild(name, nil).ReportUndeclared("property")
delete(map_, key)
}
}
// Render properties
for key, definition := range dataType.PropertyDefinitions {
if data, ok := map_[key]; ok {
var value *Value
if value, ok = data.(*Value); !ok {
// Convert to value
value = ReadValue(self.Context.MapChild(key, data)).(*Value)
map_[key] = value
}
if definition.DataType != nil {
value.RenderProperty(definition.DataType, definition)
}
// Grab information
if value.Information != nil {
if !value.Information.Empty() {
self.Information.Properties[key] = value.Information
}
}
} else if definition.IsRequired() {
self.Context.MapChild(key, data).ReportPropertyRequired("property")
}
}
}
}
func (self *Value) RenderProperty(dataType *DataType, definition *PropertyDefinition) {
if definition == nil {
self.RenderAttribute(dataType, nil, false, false)
} else {
self.ConstraintClauses.Render(dataType)
definition.ConstraintClauses.Render(definition.DataType)
self.ConstraintClauses = definition.ConstraintClauses.Append(self.ConstraintClauses)
self.RenderAttribute(dataType, definition.AttributeDefinition, false, false)
//definition.ConstraintClauses.Prepend(&self.ConstraintClauses, dataType)
}
}
func ReadAndRenderBareAttribute(context *tosca.Context, dataType *DataType) *Value {
self := ReadValue(context).(*Value)
self.RenderAttribute(dataType, nil, true, false)
return self
}
func (self *Value) Normalize() normal.Constrainable {
return self.normalize(true)
}
func (self *Value) normalize(withInformation bool) normal.Constrainable {
var normalConstrainable normal.Constrainable
switch data := self.Context.Data.(type) {
case ard.Map:
// This is for complex types (the "map" type is a ValueMap, below)
normalMap := normal.NewMap()
for key, value := range data {
if v, ok := value.(*Value); ok {
normalMap.Put(key, v.normalize(false))
} else {
normalMap.Put(key, normal.NewValue(value))
}
}
normalConstrainable = normalMap
case *ValueList:
normalConstrainable = data.Normalize(self.Context)
case *ValueMap:
normalConstrainable = data.Normalize(self.Context)
case *tosca.FunctionCall:
NormalizeFunctionCallArguments(data, self.Context)
normalConstrainable = normal.NewFunctionCall(data)
default:
value := normal.NewValue(data)
normalConstrainable = value
}
if withInformation {
normalConstrainable.SetInformation(self.Information)
}
self.ConstraintClauses.NormalizeConstrainable(self.Context, normalConstrainable)
return normalConstrainable
}
//
// Values
//
type Values map[string]*Value
func (self Values) CopyUnassigned(values Values) {
for key, value := range values {
if _, ok := self[key]; !ok {
self[key] = value
}
}
}
func (self Values) RenderMissingValue(definition *AttributeDefinition, kind string, required bool, context *tosca.Context) {
if definition.Default != nil {
// Note: it doesn't make sense if required=false for properties in this case,
// but we will just ignore it (future versions of TOSCA may specifically disallow it)
self[definition.Name] = definition.Default
} else if required {
// Attributes are always required=false
context.MapChild(definition.Name, nil).ReportPropertyRequired(kind)
} else if kind == "attribute" {
// Attributes should always appear, even if they have no default value
self[definition.Name] = NewValue(context.MapChild(definition.Name, nil))
}
}
func (self Values) RenderProperties(definitions PropertyDefinitions, kind string, context *tosca.Context) {
for key, definition := range definitions {
if value, ok := self[key]; !ok {
self.RenderMissingValue(definition.AttributeDefinition, kind, definition.IsRequired(), context)
// (If the above assigns the "default" value -- it has already been rendered elsewhere)
} else if definition.DataType != nil {
value.RenderProperty(definition.DataType, definition)
}
}
for key, value := range self {
if _, ok := definitions[key]; !ok {
value.Context.ReportUndeclared(kind)
delete(self, key)
}
}
}
func (self Values) RenderAttributes(definitions AttributeDefinitions, context *tosca.Context) {
for key, definition := range definitions {
if _, ok := self[key]; !ok {
self.RenderMissingValue(definition, "attribute", false, context)
}
}
for key, value := range self {
if definition, ok := definitions[key]; !ok {
value.Context.ReportUndeclared("attribute")
delete(self, key)
} else if definition.DataType != nil {
value.RenderAttribute(definition.DataType, definition, false, true)
}
}
}
func (self Values) Normalize(normalConstrainables normal.Constrainables) {
for key, value := range self {
normalConstrainables[key] = value.Normalize()
}
}
//
// ValueList
//
type ValueList struct {
EntryConstraints ConstraintClauses
Slice []interface{}
}
func NewValueList(definition *AttributeDefinition, length int, entryConstraints ConstraintClauses) *ValueList {
return &ValueList{
EntryConstraints: entryConstraints,
Slice: make([]interface{}, length),
}
}
func (self *ValueList) Set(index int, value interface{}) {
self.Slice[index] = value
}
func (self *ValueList) Normalize(context *tosca.Context) *normal.List {
normalList := normal.NewList(len(self.Slice))
self.EntryConstraints.NormalizeListEntries(context, normalList)
for index, value := range self.Slice {
if v, ok := value.(*Value); ok {
normalList.Set(index, v.normalize(false))
} else {
normalList.Set(index, normal.NewValue(value))
}
}
return normalList
}
//
// ValueMap
//
type ValueMap struct {
KeyConstraints ConstraintClauses
ValueConstraints ConstraintClauses
Map ard.Map
}
func NewValueMap(definition *AttributeDefinition, keyConstraints ConstraintClauses, valueConstraints ConstraintClauses) *ValueMap {
return &ValueMap{
KeyConstraints: keyConstraints,
ValueConstraints: valueConstraints,
Map: make(ard.Map),
}
}
func (self *ValueMap) Put(key interface{}, value interface{}) {
self.Map[key] = value
}
func (self *ValueMap) Normalize(context *tosca.Context) *normal.Map {
normalMap := normal.NewMap()
self.KeyConstraints.NormalizeMapKeys(context, normalMap)
self.ValueConstraints.NormalizeMapValues(context, normalMap)
for key, value := range self.Map {
if k, ok := key.(*Value); ok {
key = k.normalize(false)
}
if v, ok := value.(*Value); ok {
normalMap.Put(key, v.normalize(false))
} else {
normalMap.Put(key, normal.NewValue(value))
}
}
return normalMap
}