forked from freeconf/yang
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathresolver.go
348 lines (312 loc) · 8.82 KB
/
resolver.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
package meta
import (
"errors"
"fmt"
)
// responsiblities:
//
// 1.) resolve expands all "uses" with selected "grouping" which including
// refinements and augments. Once this is complete the grouping, augments and
// refinement statements are no longer useful and can be removed from the schema
// tree.
//
// 2.) process imports which triggers whole new, recursive chain of processing. This
// a form of resolving because imports are really just a way of grouping groupings into
// separate files
func resolve(m *Module) error {
r := &resolver{
builder: &Builder{},
inProgressUses: make(map[interface{}]HasDataDefinitions),
}
if err := r.module(m); err != nil {
return err
}
r.fillInRecursiveDefs()
return nil
}
type recursiveEntry struct {
master HasDataDefinitions
duplicate HasDataDefinitions
}
type resolver struct {
builder *Builder
inProgressUses map[interface{}]HasDataDefinitions
recursives []recursiveEntry
}
func (r *resolver) module(y *Module) error {
if y.featureSet != nil {
if err := y.featureSet.Initialize(y); err != nil {
return err
}
}
// exand all includes
if len(y.includes) > 0 {
for _, i := range y.includes {
if i.loader == nil {
return errors.New("no module loader defined")
}
var err error
var rev string
if i.rev != nil {
rev = i.rev.Ident()
}
_, err = i.loader(i.parent, i.subName, rev, i.parent.featureSet, i.loader)
if err != nil {
return errors.New(i.subName + " - " + err.Error())
}
}
}
// expand all imports first because local uses may reference groupings in other files.
if len(y.imports) > 0 {
// imports were indexed by module name, but now that we know the
// prefix, we need to reindex them
byName := y.imports
y.imports = make(map[string]*Import, len(byName))
for _, i := range byName {
if i.loader == nil {
return fmt.Errorf("%s - no module loader defined", i.moduleName)
}
if i.prefix == "" {
return fmt.Errorf("%s - prefix required on import", i.moduleName)
}
var err error
var rev string
if i.rev != nil {
rev = i.rev.Ident()
}
i.module, err = i.loader(nil, i.moduleName, rev, i.parent.featureSet, i.loader)
if err != nil {
return fmt.Errorf("%s - %s", i.moduleName, err)
}
// recurse
if err = r.module(i.module); err != nil {
return err
}
// imports were originally added by module name, but now that we know the
// prefix, we need to re-add them with proper key: prefix
y.imports[i.Prefix()] = i
}
}
// now we can go into definitions and resolve "uses"
if err := r.dataDef(y, y.popDataDefinitions()); err != nil {
return err
}
for _, a := range y.Augments() {
if err := r.expandAugment(a, y); err != nil {
return err
}
}
return nil
}
func (r *resolver) dataDef(x HasDataDefinitions, defs []Definition) error {
for _, def := range defs {
if more, err := r.addDataDef(x, def); err != nil || !more {
return err
}
}
if hasActions, valid := x.(HasActions); valid {
for _, a := range hasActions.Actions() {
if i := a.Input(); i != nil {
if err := r.dataDef(i, i.popDataDefinitions()); err != nil {
return err
}
}
if o := a.Output(); o != nil {
if err := r.dataDef(o, o.popDataDefinitions()); err != nil {
return err
}
}
}
}
if hasNotification, valid := x.(HasNotifications); valid {
for _, n := range hasNotification.Notifications() {
if err := r.dataDef(n, n.popDataDefinitions()); err != nil {
return err
}
}
}
return nil
}
func (r *resolver) addDataDef(parent HasDataDefinitions, child Definition) (bool, error) {
if hasIf, valid := child.(HasIfFeatures); valid {
if on, err := checkFeature(hasIf); err != nil || !on {
return true, err
}
}
if u, isUses := child.(*Uses); isUses {
g := r.findGrouping(u)
if g == nil {
return false, fmt.Errorf("%s - %s group not found", SchemaPath(u), u.ident)
}
if master, recursionDetected := r.inProgressUses[u.schemaId]; recursionDetected {
// resolve this uses later
r.recursives = append(r.recursives, recursiveEntry{master, parent})
//fmt.Printf("%s : %s <= %s \n", u.ident, SchemaPath(master), SchemaPath(parent))
return false, nil
}
r.inProgressUses[u.schemaId] = parent
// resolve all children
groupDefs := r.cloneDefs(parent, g.DataDefinitions(), u.when)
err := r.dataDef(parent, groupDefs)
if err != nil {
return false, err
}
if err := r.applyRefinements(u, parent); err != nil {
return false, err
}
for _, a := range u.augments {
if err := r.expandAugment(a, parent); err != nil {
return false, err
}
}
// copy in any actions or notifications unresolved, they will be resolved
// in caller loop
for _, a := range g.Actions() {
hasActions, validActions := parent.(HasActions)
if !validActions {
return false, fmt.Errorf("cannot add %s. %s does not allow actions", u.ident, SchemaPath(u))
}
hasActions.addAction(a.clone(parent).(*Rpc))
}
for _, a := range g.Notifications() {
hasNotifs, validNotifs := parent.(HasNotifications)
if !validNotifs {
return false, fmt.Errorf("cannot add %s. %s does not allow notifications", u.ident, SchemaPath(u))
}
hasNotifs.addNotification(a.clone(parent).(*Notification))
}
return true, nil
}
parent.addDataDefinition(child)
if h, recurse := child.(HasDataDefinitions); recurse {
if err := r.dataDef(h, h.popDataDefinitions()); err != nil {
return false, err
}
}
if choice, isChoice := child.(*Choice); isChoice {
for _, k := range choice.Cases() {
if err := r.dataDef(k, k.popDataDefinitions()); err != nil {
return false, err
}
}
}
return true, nil
}
// copy top-level children into lower-level parent and mark
// lower-level parent as recurisive
func (r *resolver) fillInRecursiveDefs() {
for _, entry := range r.recursives {
entry.duplicate.popDataDefinitions()
entry.duplicate.markRecursive()
for _, def := range entry.master.DataDefinitions() {
entry.duplicate.addDataDefinition(def)
}
}
}
func (r *resolver) cloneDefs(parent HasDataDefinitions, defs []Definition, when *When) []Definition {
copy := make([]Definition, len(defs))
for i, d := range defs {
copy[i] = d.(cloneable).clone(parent).(Definition)
if when != nil {
copy[i].(HasWhen).setWhen(when)
}
}
return copy
}
func (r *resolver) findGrouping(y *Uses) *Grouping {
// lazy load grouping
if xMod, xIdent, err := externalModule(y, y.ident); err != nil {
return nil
} else if xMod != nil {
return xMod.Groupings()[xIdent]
} else {
p := y.scopedParent()
for p != nil {
if hasGroups, ok := p.(HasGroupings); ok {
if g, found := hasGroups.Groupings()[y.ident]; found {
return g
}
}
if hasScoped, ok := p.(cloneable); ok {
p = hasScoped.scopedParent()
} else {
p = p.Parent()
}
}
}
return nil
}
func (r *resolver) applyRefinements(u *Uses, parent Definition) error {
for _, refine := range u.refines {
if on, err := checkFeature(refine); !on || err != nil {
return err
}
target := Find(parent.(HasDataDefinitions), refine.Ident())
if target == nil {
return fmt.Errorf("%s:could not find target for refine %s", SchemaPath(u), refine.Ident())
}
if err := r.refine(target, refine); err != nil {
return err
}
}
return nil
}
func (r *resolver) refine(target Definition, y *Refine) error {
if y.desc != "" {
r.builder.Description(target, y.desc)
}
if y.ref != "" {
r.builder.Reference(target, y.ref)
}
if y.defaultVal != nil {
r.builder.Default(target, y.defaultVal)
}
if y.configPtr != nil {
r.builder.Config(target, *y.configPtr)
}
if y.mandatoryPtr != nil {
r.builder.Mandatory(target, *y.mandatoryPtr)
}
if y.maxElementsPtr != nil {
r.builder.MaxElements(target, *y.maxElementsPtr)
}
if y.minElementsPtr != nil {
r.builder.MinElements(target, *y.minElementsPtr)
}
if y.unboundedPtr != nil {
r.builder.UnBounded(target, *y.unboundedPtr)
}
for _, m := range y.Musts() {
h, valid := target.(HasMusts)
if !valid {
r.builder.setErr(fmt.Errorf("%T does not support must", target))
} else {
h.addMust(m.clone(target).(*Must))
}
}
return r.builder.LastErr
}
func (r *resolver) expandAugment(y *Augment, parent Meta) error {
if on, err := checkFeature(y); !on || err != nil {
return err
}
// RFC7950 Sec 7.17
// "The target node MUST be either a container, list, choice, case, input,
// output, or notification node."
target := Find(parent.(HasDataDefinitions), y.ident)
if target == nil {
return errors.New(SchemaPath(y) + " - augment target is not found " + y.ident)
}
// expand
for _, x := range y.actions {
target.(HasActions).addAction(x.clone(target).(*Rpc))
}
for _, x := range y.notifications {
target.(HasNotifications).addNotification(x.clone(target).(*Notification))
}
for _, x := range y.dataDefs {
copy := x.(cloneable).clone(target).(Definition)
target.(HasDataDefinitions).addDataDefinition(copy)
}
return nil
}