blob: 31cc7be38b3cae06152583eb904c4da732b4223b [file] [log] [blame]
Don Newton98fd8812019-09-23 15:15:02 -04001// Copyright 2013 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package cldr
6
7// This file implements the various inheritance constructs defined by LDML.
8// See https://www.unicode.org/reports/tr35/#Inheritance_and_Validity
9// for more details.
10
11import (
12 "fmt"
13 "log"
14 "reflect"
15 "regexp"
16 "sort"
17 "strings"
18)
19
20// fieldIter iterates over fields in a struct. It includes
21// fields of embedded structs.
22type fieldIter struct {
23 v reflect.Value
24 index, n []int
25}
26
27func iter(v reflect.Value) fieldIter {
28 if v.Kind() != reflect.Struct {
29 log.Panicf("value %v must be a struct", v)
30 }
31 i := fieldIter{
32 v: v,
33 index: []int{0},
34 n: []int{v.NumField()},
35 }
36 i.descent()
37 return i
38}
39
40func (i *fieldIter) descent() {
41 for f := i.field(); f.Anonymous && f.Type.NumField() > 0; f = i.field() {
42 i.index = append(i.index, 0)
43 i.n = append(i.n, f.Type.NumField())
44 }
45}
46
47func (i *fieldIter) done() bool {
48 return len(i.index) == 1 && i.index[0] >= i.n[0]
49}
50
51func skip(f reflect.StructField) bool {
52 return !f.Anonymous && (f.Name[0] < 'A' || f.Name[0] > 'Z')
53}
54
55func (i *fieldIter) next() {
56 for {
57 k := len(i.index) - 1
58 i.index[k]++
59 if i.index[k] < i.n[k] {
60 if !skip(i.field()) {
61 break
62 }
63 } else {
64 if k == 0 {
65 return
66 }
67 i.index = i.index[:k]
68 i.n = i.n[:k]
69 }
70 }
71 i.descent()
72}
73
74func (i *fieldIter) value() reflect.Value {
75 return i.v.FieldByIndex(i.index)
76}
77
78func (i *fieldIter) field() reflect.StructField {
79 return i.v.Type().FieldByIndex(i.index)
80}
81
82type visitor func(v reflect.Value) error
83
84var stopDescent = fmt.Errorf("do not recurse")
85
86func (f visitor) visit(x interface{}) error {
87 return f.visitRec(reflect.ValueOf(x))
88}
89
90// visit recursively calls f on all nodes in v.
91func (f visitor) visitRec(v reflect.Value) error {
92 if v.Kind() == reflect.Ptr {
93 if v.IsNil() {
94 return nil
95 }
96 return f.visitRec(v.Elem())
97 }
98 if err := f(v); err != nil {
99 if err == stopDescent {
100 return nil
101 }
102 return err
103 }
104 switch v.Kind() {
105 case reflect.Struct:
106 for i := iter(v); !i.done(); i.next() {
107 if err := f.visitRec(i.value()); err != nil {
108 return err
109 }
110 }
111 case reflect.Slice:
112 for i := 0; i < v.Len(); i++ {
113 if err := f.visitRec(v.Index(i)); err != nil {
114 return err
115 }
116 }
117 }
118 return nil
119}
120
121// getPath is used for error reporting purposes only.
122func getPath(e Elem) string {
123 if e == nil {
124 return "<nil>"
125 }
126 if e.enclosing() == nil {
127 return e.GetCommon().name
128 }
129 if e.GetCommon().Type == "" {
130 return fmt.Sprintf("%s.%s", getPath(e.enclosing()), e.GetCommon().name)
131 }
132 return fmt.Sprintf("%s.%s[type=%s]", getPath(e.enclosing()), e.GetCommon().name, e.GetCommon().Type)
133}
134
135// xmlName returns the xml name of the element or attribute
136func xmlName(f reflect.StructField) (name string, attr bool) {
137 tags := strings.Split(f.Tag.Get("xml"), ",")
138 for _, s := range tags {
139 attr = attr || s == "attr"
140 }
141 return tags[0], attr
142}
143
144func findField(v reflect.Value, key string) (reflect.Value, error) {
145 v = reflect.Indirect(v)
146 for i := iter(v); !i.done(); i.next() {
147 if n, _ := xmlName(i.field()); n == key {
148 return i.value(), nil
149 }
150 }
151 return reflect.Value{}, fmt.Errorf("cldr: no field %q in element %#v", key, v.Interface())
152}
153
154var xpathPart = regexp.MustCompile(`(\pL+)(?:\[@(\pL+)='([\w-]+)'\])?`)
155
156func walkXPath(e Elem, path string) (res Elem, err error) {
157 for _, c := range strings.Split(path, "/") {
158 if c == ".." {
159 if e = e.enclosing(); e == nil {
160 panic("path ..")
161 return nil, fmt.Errorf(`cldr: ".." moves past root in path %q`, path)
162 }
163 continue
164 } else if c == "" {
165 continue
166 }
167 m := xpathPart.FindStringSubmatch(c)
168 if len(m) == 0 || len(m[0]) != len(c) {
169 return nil, fmt.Errorf("cldr: syntax error in path component %q", c)
170 }
171 v, err := findField(reflect.ValueOf(e), m[1])
172 if err != nil {
173 return nil, err
174 }
175 switch v.Kind() {
176 case reflect.Slice:
177 i := 0
178 if m[2] != "" || v.Len() > 1 {
179 if m[2] == "" {
180 m[2] = "type"
181 if m[3] = e.GetCommon().Default(); m[3] == "" {
182 return nil, fmt.Errorf("cldr: type selector or default value needed for element %s", m[1])
183 }
184 }
185 for ; i < v.Len(); i++ {
186 vi := v.Index(i)
187 key, err := findField(vi.Elem(), m[2])
188 if err != nil {
189 return nil, err
190 }
191 key = reflect.Indirect(key)
192 if key.Kind() == reflect.String && key.String() == m[3] {
193 break
194 }
195 }
196 }
197 if i == v.Len() || v.Index(i).IsNil() {
198 return nil, fmt.Errorf("no %s found with %s==%s", m[1], m[2], m[3])
199 }
200 e = v.Index(i).Interface().(Elem)
201 case reflect.Ptr:
202 if v.IsNil() {
203 return nil, fmt.Errorf("cldr: element %q not found within element %q", m[1], e.GetCommon().name)
204 }
205 var ok bool
206 if e, ok = v.Interface().(Elem); !ok {
207 return nil, fmt.Errorf("cldr: %q is not an XML element", m[1])
208 } else if m[2] != "" || m[3] != "" {
209 return nil, fmt.Errorf("cldr: no type selector allowed for element %s", m[1])
210 }
211 default:
212 return nil, fmt.Errorf("cldr: %q is not an XML element", m[1])
213 }
214 }
215 return e, nil
216}
217
218const absPrefix = "//ldml/"
219
220func (cldr *CLDR) resolveAlias(e Elem, src, path string) (res Elem, err error) {
221 if src != "locale" {
222 if !strings.HasPrefix(path, absPrefix) {
223 return nil, fmt.Errorf("cldr: expected absolute path, found %q", path)
224 }
225 path = path[len(absPrefix):]
226 if e, err = cldr.resolve(src); err != nil {
227 return nil, err
228 }
229 }
230 return walkXPath(e, path)
231}
232
233func (cldr *CLDR) resolveAndMergeAlias(e Elem) error {
234 alias := e.GetCommon().Alias
235 if alias == nil {
236 return nil
237 }
238 a, err := cldr.resolveAlias(e, alias.Source, alias.Path)
239 if err != nil {
240 return fmt.Errorf("%v: error evaluating path %q: %v", getPath(e), alias.Path, err)
241 }
242 // Ensure alias node was already evaluated. TODO: avoid double evaluation.
243 err = cldr.resolveAndMergeAlias(a)
244 v := reflect.ValueOf(e).Elem()
245 for i := iter(reflect.ValueOf(a).Elem()); !i.done(); i.next() {
246 if vv := i.value(); vv.Kind() != reflect.Ptr || !vv.IsNil() {
247 if _, attr := xmlName(i.field()); !attr {
248 v.FieldByIndex(i.index).Set(vv)
249 }
250 }
251 }
252 return err
253}
254
255func (cldr *CLDR) aliasResolver() visitor {
256 return func(v reflect.Value) (err error) {
257 if e, ok := v.Addr().Interface().(Elem); ok {
258 err = cldr.resolveAndMergeAlias(e)
259 if err == nil && blocking[e.GetCommon().name] {
260 return stopDescent
261 }
262 }
263 return err
264 }
265}
266
267// elements within blocking elements do not inherit.
268// Taken from CLDR's supplementalMetaData.xml.
269var blocking = map[string]bool{
270 "identity": true,
271 "supplementalData": true,
272 "cldrTest": true,
273 "collation": true,
274 "transform": true,
275}
276
277// Distinguishing attributes affect inheritance; two elements with different
278// distinguishing attributes are treated as different for purposes of inheritance,
279// except when such attributes occur in the indicated elements.
280// Taken from CLDR's supplementalMetaData.xml.
281var distinguishing = map[string][]string{
282 "key": nil,
283 "request_id": nil,
284 "id": nil,
285 "registry": nil,
286 "alt": nil,
287 "iso4217": nil,
288 "iso3166": nil,
289 "mzone": nil,
290 "from": nil,
291 "to": nil,
292 "type": []string{
293 "abbreviationFallback",
294 "default",
295 "mapping",
296 "measurementSystem",
297 "preferenceOrdering",
298 },
299 "numberSystem": nil,
300}
301
302func in(set []string, s string) bool {
303 for _, v := range set {
304 if v == s {
305 return true
306 }
307 }
308 return false
309}
310
311// attrKey computes a key based on the distinguishable attributes of
312// an element and its values.
313func attrKey(v reflect.Value, exclude ...string) string {
314 parts := []string{}
315 ename := v.Interface().(Elem).GetCommon().name
316 v = v.Elem()
317 for i := iter(v); !i.done(); i.next() {
318 if name, attr := xmlName(i.field()); attr {
319 if except, ok := distinguishing[name]; ok && !in(exclude, name) && !in(except, ename) {
320 v := i.value()
321 if v.Kind() == reflect.Ptr {
322 v = v.Elem()
323 }
324 if v.IsValid() {
325 parts = append(parts, fmt.Sprintf("%s=%s", name, v.String()))
326 }
327 }
328 }
329 }
330 sort.Strings(parts)
331 return strings.Join(parts, ";")
332}
333
334// Key returns a key for e derived from all distinguishing attributes
335// except those specified by exclude.
336func Key(e Elem, exclude ...string) string {
337 return attrKey(reflect.ValueOf(e), exclude...)
338}
339
340// linkEnclosing sets the enclosing element as well as the name
341// for all sub-elements of child, recursively.
342func linkEnclosing(parent, child Elem) {
343 child.setEnclosing(parent)
344 v := reflect.ValueOf(child).Elem()
345 for i := iter(v); !i.done(); i.next() {
346 vf := i.value()
347 if vf.Kind() == reflect.Slice {
348 for j := 0; j < vf.Len(); j++ {
349 linkEnclosing(child, vf.Index(j).Interface().(Elem))
350 }
351 } else if vf.Kind() == reflect.Ptr && !vf.IsNil() && vf.Elem().Kind() == reflect.Struct {
352 linkEnclosing(child, vf.Interface().(Elem))
353 }
354 }
355}
356
357func setNames(e Elem, name string) {
358 e.setName(name)
359 v := reflect.ValueOf(e).Elem()
360 for i := iter(v); !i.done(); i.next() {
361 vf := i.value()
362 name, _ = xmlName(i.field())
363 if vf.Kind() == reflect.Slice {
364 for j := 0; j < vf.Len(); j++ {
365 setNames(vf.Index(j).Interface().(Elem), name)
366 }
367 } else if vf.Kind() == reflect.Ptr && !vf.IsNil() && vf.Elem().Kind() == reflect.Struct {
368 setNames(vf.Interface().(Elem), name)
369 }
370 }
371}
372
373// deepCopy copies elements of v recursively. All elements of v that may
374// be modified by inheritance are explicitly copied.
375func deepCopy(v reflect.Value) reflect.Value {
376 switch v.Kind() {
377 case reflect.Ptr:
378 if v.IsNil() || v.Elem().Kind() != reflect.Struct {
379 return v
380 }
381 nv := reflect.New(v.Elem().Type())
382 nv.Elem().Set(v.Elem())
383 deepCopyRec(nv.Elem(), v.Elem())
384 return nv
385 case reflect.Slice:
386 nv := reflect.MakeSlice(v.Type(), v.Len(), v.Len())
387 for i := 0; i < v.Len(); i++ {
388 deepCopyRec(nv.Index(i), v.Index(i))
389 }
390 return nv
391 }
392 panic("deepCopy: must be called with pointer or slice")
393}
394
395// deepCopyRec is only called by deepCopy.
396func deepCopyRec(nv, v reflect.Value) {
397 if v.Kind() == reflect.Struct {
398 t := v.Type()
399 for i := 0; i < v.NumField(); i++ {
400 if name, attr := xmlName(t.Field(i)); name != "" && !attr {
401 deepCopyRec(nv.Field(i), v.Field(i))
402 }
403 }
404 } else {
405 nv.Set(deepCopy(v))
406 }
407}
408
409// newNode is used to insert a missing node during inheritance.
410func (cldr *CLDR) newNode(v, enc reflect.Value) reflect.Value {
411 n := reflect.New(v.Type())
412 for i := iter(v); !i.done(); i.next() {
413 if name, attr := xmlName(i.field()); name == "" || attr {
414 n.Elem().FieldByIndex(i.index).Set(i.value())
415 }
416 }
417 n.Interface().(Elem).GetCommon().setEnclosing(enc.Addr().Interface().(Elem))
418 return n
419}
420
421// v, parent must be pointers to struct
422func (cldr *CLDR) inheritFields(v, parent reflect.Value) (res reflect.Value, err error) {
423 t := v.Type()
424 nv := reflect.New(t)
425 nv.Elem().Set(v)
426 for i := iter(v); !i.done(); i.next() {
427 vf := i.value()
428 f := i.field()
429 name, attr := xmlName(f)
430 if name == "" || attr {
431 continue
432 }
433 pf := parent.FieldByIndex(i.index)
434 if blocking[name] {
435 if vf.IsNil() {
436 vf = pf
437 }
438 nv.Elem().FieldByIndex(i.index).Set(deepCopy(vf))
439 continue
440 }
441 switch f.Type.Kind() {
442 case reflect.Ptr:
443 if f.Type.Elem().Kind() == reflect.Struct {
444 if !vf.IsNil() {
445 if vf, err = cldr.inheritStructPtr(vf, pf); err != nil {
446 return reflect.Value{}, err
447 }
448 vf.Interface().(Elem).setEnclosing(nv.Interface().(Elem))
449 nv.Elem().FieldByIndex(i.index).Set(vf)
450 } else if !pf.IsNil() {
451 n := cldr.newNode(pf.Elem(), v)
452 if vf, err = cldr.inheritStructPtr(n, pf); err != nil {
453 return reflect.Value{}, err
454 }
455 vf.Interface().(Elem).setEnclosing(nv.Interface().(Elem))
456 nv.Elem().FieldByIndex(i.index).Set(vf)
457 }
458 }
459 case reflect.Slice:
460 vf, err := cldr.inheritSlice(nv.Elem(), vf, pf)
461 if err != nil {
462 return reflect.Zero(t), err
463 }
464 nv.Elem().FieldByIndex(i.index).Set(vf)
465 }
466 }
467 return nv, nil
468}
469
470func root(e Elem) *LDML {
471 for ; e.enclosing() != nil; e = e.enclosing() {
472 }
473 return e.(*LDML)
474}
475
476// inheritStructPtr first merges possible aliases in with v and then inherits
477// any underspecified elements from parent.
478func (cldr *CLDR) inheritStructPtr(v, parent reflect.Value) (r reflect.Value, err error) {
479 if !v.IsNil() {
480 e := v.Interface().(Elem).GetCommon()
481 alias := e.Alias
482 if alias == nil && !parent.IsNil() {
483 alias = parent.Interface().(Elem).GetCommon().Alias
484 }
485 if alias != nil {
486 a, err := cldr.resolveAlias(v.Interface().(Elem), alias.Source, alias.Path)
487 if a != nil {
488 if v, err = cldr.inheritFields(v.Elem(), reflect.ValueOf(a).Elem()); err != nil {
489 return reflect.Value{}, err
490 }
491 }
492 }
493 if !parent.IsNil() {
494 return cldr.inheritFields(v.Elem(), parent.Elem())
495 }
496 } else if parent.IsNil() {
497 panic("should not reach here")
498 }
499 return v, nil
500}
501
502// Must be slice of struct pointers.
503func (cldr *CLDR) inheritSlice(enc, v, parent reflect.Value) (res reflect.Value, err error) {
504 t := v.Type()
505 index := make(map[string]reflect.Value)
506 if !v.IsNil() {
507 for i := 0; i < v.Len(); i++ {
508 vi := v.Index(i)
509 key := attrKey(vi)
510 index[key] = vi
511 }
512 }
513 if !parent.IsNil() {
514 for i := 0; i < parent.Len(); i++ {
515 vi := parent.Index(i)
516 key := attrKey(vi)
517 if w, ok := index[key]; ok {
518 index[key], err = cldr.inheritStructPtr(w, vi)
519 } else {
520 n := cldr.newNode(vi.Elem(), enc)
521 index[key], err = cldr.inheritStructPtr(n, vi)
522 }
523 index[key].Interface().(Elem).setEnclosing(enc.Addr().Interface().(Elem))
524 if err != nil {
525 return v, err
526 }
527 }
528 }
529 keys := make([]string, 0, len(index))
530 for k, _ := range index {
531 keys = append(keys, k)
532 }
533 sort.Strings(keys)
534 sl := reflect.MakeSlice(t, len(index), len(index))
535 for i, k := range keys {
536 sl.Index(i).Set(index[k])
537 }
538 return sl, nil
539}
540
541func parentLocale(loc string) string {
542 parts := strings.Split(loc, "_")
543 if len(parts) == 1 {
544 return "root"
545 }
546 parts = parts[:len(parts)-1]
547 key := strings.Join(parts, "_")
548 return key
549}
550
551func (cldr *CLDR) resolve(loc string) (res *LDML, err error) {
552 if r := cldr.resolved[loc]; r != nil {
553 return r, nil
554 }
555 x := cldr.RawLDML(loc)
556 if x == nil {
557 return nil, fmt.Errorf("cldr: unknown locale %q", loc)
558 }
559 var v reflect.Value
560 if loc == "root" {
561 x = deepCopy(reflect.ValueOf(x)).Interface().(*LDML)
562 linkEnclosing(nil, x)
563 err = cldr.aliasResolver().visit(x)
564 } else {
565 key := parentLocale(loc)
566 var parent *LDML
567 for ; cldr.locale[key] == nil; key = parentLocale(key) {
568 }
569 if parent, err = cldr.resolve(key); err != nil {
570 return nil, err
571 }
572 v, err = cldr.inheritFields(reflect.ValueOf(x).Elem(), reflect.ValueOf(parent).Elem())
573 x = v.Interface().(*LDML)
574 linkEnclosing(nil, x)
575 }
576 if err != nil {
577 return nil, err
578 }
579 cldr.resolved[loc] = x
580 return x, err
581}
582
583// finalize finalizes the initialization of the raw LDML structs. It also
584// removed unwanted fields, as specified by filter, so that they will not
585// be unnecessarily evaluated.
586func (cldr *CLDR) finalize(filter []string) {
587 for _, x := range cldr.locale {
588 if filter != nil {
589 v := reflect.ValueOf(x).Elem()
590 t := v.Type()
591 for i := 0; i < v.NumField(); i++ {
592 f := t.Field(i)
593 name, _ := xmlName(f)
594 if name != "" && name != "identity" && !in(filter, name) {
595 v.Field(i).Set(reflect.Zero(f.Type))
596 }
597 }
598 }
599 linkEnclosing(nil, x) // for resolving aliases and paths
600 setNames(x, "ldml")
601 }
602}