| // Copyright 2013 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package cldr | |
| // This file implements the various inheritance constructs defined by LDML. | |
| // See http://www.unicode.org/reports/tr35/#Inheritance_and_Validity | |
| // for more details. | |
| import ( | |
| "fmt" | |
| "log" | |
| "reflect" | |
| "regexp" | |
| "sort" | |
| "strings" | |
| ) | |
| // fieldIter iterates over fields in a struct. It includes | |
| // fields of embedded structs. | |
| type fieldIter struct { | |
| v reflect.Value | |
| index, n []int | |
| } | |
| func iter(v reflect.Value) fieldIter { | |
| if v.Kind() != reflect.Struct { | |
| log.Panicf("value %v must be a struct", v) | |
| } | |
| i := fieldIter{ | |
| v: v, | |
| index: []int{0}, | |
| n: []int{v.NumField()}, | |
| } | |
| i.descent() | |
| return i | |
| } | |
| func (i *fieldIter) descent() { | |
| for f := i.field(); f.Anonymous && f.Type.NumField() > 0; f = i.field() { | |
| i.index = append(i.index, 0) | |
| i.n = append(i.n, f.Type.NumField()) | |
| } | |
| } | |
| func (i *fieldIter) done() bool { | |
| return len(i.index) == 1 && i.index[0] >= i.n[0] | |
| } | |
| func skip(f reflect.StructField) bool { | |
| return !f.Anonymous && (f.Name[0] < 'A' || f.Name[0] > 'Z') | |
| } | |
| func (i *fieldIter) next() { | |
| for { | |
| k := len(i.index) - 1 | |
| i.index[k]++ | |
| if i.index[k] < i.n[k] { | |
| if !skip(i.field()) { | |
| break | |
| } | |
| } else { | |
| if k == 0 { | |
| return | |
| } | |
| i.index = i.index[:k] | |
| i.n = i.n[:k] | |
| } | |
| } | |
| i.descent() | |
| } | |
| func (i *fieldIter) value() reflect.Value { | |
| return i.v.FieldByIndex(i.index) | |
| } | |
| func (i *fieldIter) field() reflect.StructField { | |
| return i.v.Type().FieldByIndex(i.index) | |
| } | |
| type visitor func(v reflect.Value) error | |
| var stopDescent = fmt.Errorf("do not recurse") | |
| func (f visitor) visit(x interface{}) error { | |
| return f.visitRec(reflect.ValueOf(x)) | |
| } | |
| // visit recursively calls f on all nodes in v. | |
| func (f visitor) visitRec(v reflect.Value) error { | |
| if v.Kind() == reflect.Ptr { | |
| if v.IsNil() { | |
| return nil | |
| } | |
| return f.visitRec(v.Elem()) | |
| } | |
| if err := f(v); err != nil { | |
| if err == stopDescent { | |
| return nil | |
| } | |
| return err | |
| } | |
| switch v.Kind() { | |
| case reflect.Struct: | |
| for i := iter(v); !i.done(); i.next() { | |
| if err := f.visitRec(i.value()); err != nil { | |
| return err | |
| } | |
| } | |
| case reflect.Slice: | |
| for i := 0; i < v.Len(); i++ { | |
| if err := f.visitRec(v.Index(i)); err != nil { | |
| return err | |
| } | |
| } | |
| } | |
| return nil | |
| } | |
| // getPath is used for error reporting purposes only. | |
| func getPath(e Elem) string { | |
| if e == nil { | |
| return "<nil>" | |
| } | |
| if e.enclosing() == nil { | |
| return e.GetCommon().name | |
| } | |
| if e.GetCommon().Type == "" { | |
| return fmt.Sprintf("%s.%s", getPath(e.enclosing()), e.GetCommon().name) | |
| } | |
| return fmt.Sprintf("%s.%s[type=%s]", getPath(e.enclosing()), e.GetCommon().name, e.GetCommon().Type) | |
| } | |
| // xmlName returns the xml name of the element or attribute | |
| func xmlName(f reflect.StructField) (name string, attr bool) { | |
| tags := strings.Split(f.Tag.Get("xml"), ",") | |
| for _, s := range tags { | |
| attr = attr || s == "attr" | |
| } | |
| return tags[0], attr | |
| } | |
| func findField(v reflect.Value, key string) (reflect.Value, error) { | |
| v = reflect.Indirect(v) | |
| for i := iter(v); !i.done(); i.next() { | |
| if n, _ := xmlName(i.field()); n == key { | |
| return i.value(), nil | |
| } | |
| } | |
| return reflect.Value{}, fmt.Errorf("cldr: no field %q in element %#v", key, v.Interface()) | |
| } | |
| var xpathPart = regexp.MustCompile(`(\pL+)(?:\[@(\pL+)='([\w-]+)'\])?`) | |
| func walkXPath(e Elem, path string) (res Elem, err error) { | |
| for _, c := range strings.Split(path, "/") { | |
| if c == ".." { | |
| if e = e.enclosing(); e == nil { | |
| panic("path ..") | |
| return nil, fmt.Errorf(`cldr: ".." moves past root in path %q`, path) | |
| } | |
| continue | |
| } else if c == "" { | |
| continue | |
| } | |
| m := xpathPart.FindStringSubmatch(c) | |
| if len(m) == 0 || len(m[0]) != len(c) { | |
| return nil, fmt.Errorf("cldr: syntax error in path component %q", c) | |
| } | |
| v, err := findField(reflect.ValueOf(e), m[1]) | |
| if err != nil { | |
| return nil, err | |
| } | |
| switch v.Kind() { | |
| case reflect.Slice: | |
| i := 0 | |
| if m[2] != "" || v.Len() > 1 { | |
| if m[2] == "" { | |
| m[2] = "type" | |
| if m[3] = e.GetCommon().Default(); m[3] == "" { | |
| return nil, fmt.Errorf("cldr: type selector or default value needed for element %s", m[1]) | |
| } | |
| } | |
| for ; i < v.Len(); i++ { | |
| vi := v.Index(i) | |
| key, err := findField(vi.Elem(), m[2]) | |
| if err != nil { | |
| return nil, err | |
| } | |
| key = reflect.Indirect(key) | |
| if key.Kind() == reflect.String && key.String() == m[3] { | |
| break | |
| } | |
| } | |
| } | |
| if i == v.Len() || v.Index(i).IsNil() { | |
| return nil, fmt.Errorf("no %s found with %s==%s", m[1], m[2], m[3]) | |
| } | |
| e = v.Index(i).Interface().(Elem) | |
| case reflect.Ptr: | |
| if v.IsNil() { | |
| return nil, fmt.Errorf("cldr: element %q not found within element %q", m[1], e.GetCommon().name) | |
| } | |
| var ok bool | |
| if e, ok = v.Interface().(Elem); !ok { | |
| return nil, fmt.Errorf("cldr: %q is not an XML element", m[1]) | |
| } else if m[2] != "" || m[3] != "" { | |
| return nil, fmt.Errorf("cldr: no type selector allowed for element %s", m[1]) | |
| } | |
| default: | |
| return nil, fmt.Errorf("cldr: %q is not an XML element", m[1]) | |
| } | |
| } | |
| return e, nil | |
| } | |
| const absPrefix = "//ldml/" | |
| func (cldr *CLDR) resolveAlias(e Elem, src, path string) (res Elem, err error) { | |
| if src != "locale" { | |
| if !strings.HasPrefix(path, absPrefix) { | |
| return nil, fmt.Errorf("cldr: expected absolute path, found %q", path) | |
| } | |
| path = path[len(absPrefix):] | |
| if e, err = cldr.resolve(src); err != nil { | |
| return nil, err | |
| } | |
| } | |
| return walkXPath(e, path) | |
| } | |
| func (cldr *CLDR) resolveAndMergeAlias(e Elem) error { | |
| alias := e.GetCommon().Alias | |
| if alias == nil { | |
| return nil | |
| } | |
| a, err := cldr.resolveAlias(e, alias.Source, alias.Path) | |
| if err != nil { | |
| return fmt.Errorf("%v: error evaluating path %q: %v", getPath(e), alias.Path, err) | |
| } | |
| // Ensure alias node was already evaluated. TODO: avoid double evaluation. | |
| err = cldr.resolveAndMergeAlias(a) | |
| v := reflect.ValueOf(e).Elem() | |
| for i := iter(reflect.ValueOf(a).Elem()); !i.done(); i.next() { | |
| if vv := i.value(); vv.Kind() != reflect.Ptr || !vv.IsNil() { | |
| if _, attr := xmlName(i.field()); !attr { | |
| v.FieldByIndex(i.index).Set(vv) | |
| } | |
| } | |
| } | |
| return err | |
| } | |
| func (cldr *CLDR) aliasResolver() visitor { | |
| return func(v reflect.Value) (err error) { | |
| if e, ok := v.Addr().Interface().(Elem); ok { | |
| err = cldr.resolveAndMergeAlias(e) | |
| if err == nil && blocking[e.GetCommon().name] { | |
| return stopDescent | |
| } | |
| } | |
| return err | |
| } | |
| } | |
| // elements within blocking elements do not inherit. | |
| // Taken from CLDR's supplementalMetaData.xml. | |
| var blocking = map[string]bool{ | |
| "identity": true, | |
| "supplementalData": true, | |
| "cldrTest": true, | |
| "collation": true, | |
| "transform": true, | |
| } | |
| // Distinguishing attributes affect inheritance; two elements with different | |
| // distinguishing attributes are treated as different for purposes of inheritance, | |
| // except when such attributes occur in the indicated elements. | |
| // Taken from CLDR's supplementalMetaData.xml. | |
| var distinguishing = map[string][]string{ | |
| "key": nil, | |
| "request_id": nil, | |
| "id": nil, | |
| "registry": nil, | |
| "alt": nil, | |
| "iso4217": nil, | |
| "iso3166": nil, | |
| "mzone": nil, | |
| "from": nil, | |
| "to": nil, | |
| "type": []string{ | |
| "abbreviationFallback", | |
| "default", | |
| "mapping", | |
| "measurementSystem", | |
| "preferenceOrdering", | |
| }, | |
| "numberSystem": nil, | |
| } | |
| func in(set []string, s string) bool { | |
| for _, v := range set { | |
| if v == s { | |
| return true | |
| } | |
| } | |
| return false | |
| } | |
| // attrKey computes a key based on the distinguishable attributes of | |
| // an element and it's values. | |
| func attrKey(v reflect.Value, exclude ...string) string { | |
| parts := []string{} | |
| ename := v.Interface().(Elem).GetCommon().name | |
| v = v.Elem() | |
| for i := iter(v); !i.done(); i.next() { | |
| if name, attr := xmlName(i.field()); attr { | |
| if except, ok := distinguishing[name]; ok && !in(exclude, name) && !in(except, ename) { | |
| v := i.value() | |
| if v.Kind() == reflect.Ptr { | |
| v = v.Elem() | |
| } | |
| if v.IsValid() { | |
| parts = append(parts, fmt.Sprintf("%s=%s", name, v.String())) | |
| } | |
| } | |
| } | |
| } | |
| sort.Strings(parts) | |
| return strings.Join(parts, ";") | |
| } | |
| // Key returns a key for e derived from all distinguishing attributes | |
| // except those specified by exclude. | |
| func Key(e Elem, exclude ...string) string { | |
| return attrKey(reflect.ValueOf(e), exclude...) | |
| } | |
| // linkEnclosing sets the enclosing element as well as the name | |
| // for all sub-elements of child, recursively. | |
| func linkEnclosing(parent, child Elem) { | |
| child.setEnclosing(parent) | |
| v := reflect.ValueOf(child).Elem() | |
| for i := iter(v); !i.done(); i.next() { | |
| vf := i.value() | |
| if vf.Kind() == reflect.Slice { | |
| for j := 0; j < vf.Len(); j++ { | |
| linkEnclosing(child, vf.Index(j).Interface().(Elem)) | |
| } | |
| } else if vf.Kind() == reflect.Ptr && !vf.IsNil() && vf.Elem().Kind() == reflect.Struct { | |
| linkEnclosing(child, vf.Interface().(Elem)) | |
| } | |
| } | |
| } | |
| func setNames(e Elem, name string) { | |
| e.setName(name) | |
| v := reflect.ValueOf(e).Elem() | |
| for i := iter(v); !i.done(); i.next() { | |
| vf := i.value() | |
| name, _ = xmlName(i.field()) | |
| if vf.Kind() == reflect.Slice { | |
| for j := 0; j < vf.Len(); j++ { | |
| setNames(vf.Index(j).Interface().(Elem), name) | |
| } | |
| } else if vf.Kind() == reflect.Ptr && !vf.IsNil() && vf.Elem().Kind() == reflect.Struct { | |
| setNames(vf.Interface().(Elem), name) | |
| } | |
| } | |
| } | |
| // deepCopy copies elements of v recursively. All elements of v that may | |
| // be modified by inheritance are explicitly copied. | |
| func deepCopy(v reflect.Value) reflect.Value { | |
| switch v.Kind() { | |
| case reflect.Ptr: | |
| if v.IsNil() || v.Elem().Kind() != reflect.Struct { | |
| return v | |
| } | |
| nv := reflect.New(v.Elem().Type()) | |
| nv.Elem().Set(v.Elem()) | |
| deepCopyRec(nv.Elem(), v.Elem()) | |
| return nv | |
| case reflect.Slice: | |
| nv := reflect.MakeSlice(v.Type(), v.Len(), v.Len()) | |
| for i := 0; i < v.Len(); i++ { | |
| deepCopyRec(nv.Index(i), v.Index(i)) | |
| } | |
| return nv | |
| } | |
| panic("deepCopy: must be called with pointer or slice") | |
| } | |
| // deepCopyRec is only called by deepCopy. | |
| func deepCopyRec(nv, v reflect.Value) { | |
| if v.Kind() == reflect.Struct { | |
| t := v.Type() | |
| for i := 0; i < v.NumField(); i++ { | |
| if name, attr := xmlName(t.Field(i)); name != "" && !attr { | |
| deepCopyRec(nv.Field(i), v.Field(i)) | |
| } | |
| } | |
| } else { | |
| nv.Set(deepCopy(v)) | |
| } | |
| } | |
| // newNode is used to insert a missing node during inheritance. | |
| func (cldr *CLDR) newNode(v, enc reflect.Value) reflect.Value { | |
| n := reflect.New(v.Type()) | |
| for i := iter(v); !i.done(); i.next() { | |
| if name, attr := xmlName(i.field()); name == "" || attr { | |
| n.Elem().FieldByIndex(i.index).Set(i.value()) | |
| } | |
| } | |
| n.Interface().(Elem).GetCommon().setEnclosing(enc.Addr().Interface().(Elem)) | |
| return n | |
| } | |
| // v, parent must be pointers to struct | |
| func (cldr *CLDR) inheritFields(v, parent reflect.Value) (res reflect.Value, err error) { | |
| t := v.Type() | |
| nv := reflect.New(t) | |
| nv.Elem().Set(v) | |
| for i := iter(v); !i.done(); i.next() { | |
| vf := i.value() | |
| f := i.field() | |
| name, attr := xmlName(f) | |
| if name == "" || attr { | |
| continue | |
| } | |
| pf := parent.FieldByIndex(i.index) | |
| if blocking[name] { | |
| if vf.IsNil() { | |
| vf = pf | |
| } | |
| nv.Elem().FieldByIndex(i.index).Set(deepCopy(vf)) | |
| continue | |
| } | |
| switch f.Type.Kind() { | |
| case reflect.Ptr: | |
| if f.Type.Elem().Kind() == reflect.Struct { | |
| if !vf.IsNil() { | |
| if vf, err = cldr.inheritStructPtr(vf, pf); err != nil { | |
| return reflect.Value{}, err | |
| } | |
| vf.Interface().(Elem).setEnclosing(nv.Interface().(Elem)) | |
| nv.Elem().FieldByIndex(i.index).Set(vf) | |
| } else if !pf.IsNil() { | |
| n := cldr.newNode(pf.Elem(), v) | |
| if vf, err = cldr.inheritStructPtr(n, pf); err != nil { | |
| return reflect.Value{}, err | |
| } | |
| vf.Interface().(Elem).setEnclosing(nv.Interface().(Elem)) | |
| nv.Elem().FieldByIndex(i.index).Set(vf) | |
| } | |
| } | |
| case reflect.Slice: | |
| vf, err := cldr.inheritSlice(nv.Elem(), vf, pf) | |
| if err != nil { | |
| return reflect.Zero(t), err | |
| } | |
| nv.Elem().FieldByIndex(i.index).Set(vf) | |
| } | |
| } | |
| return nv, nil | |
| } | |
| func root(e Elem) *LDML { | |
| for ; e.enclosing() != nil; e = e.enclosing() { | |
| } | |
| return e.(*LDML) | |
| } | |
| // inheritStructPtr first merges possible aliases in with v and then inherits | |
| // any underspecified elements from parent. | |
| func (cldr *CLDR) inheritStructPtr(v, parent reflect.Value) (r reflect.Value, err error) { | |
| if !v.IsNil() { | |
| e := v.Interface().(Elem).GetCommon() | |
| alias := e.Alias | |
| if alias == nil && !parent.IsNil() { | |
| alias = parent.Interface().(Elem).GetCommon().Alias | |
| } | |
| if alias != nil { | |
| a, err := cldr.resolveAlias(v.Interface().(Elem), alias.Source, alias.Path) | |
| if a != nil { | |
| if v, err = cldr.inheritFields(v.Elem(), reflect.ValueOf(a).Elem()); err != nil { | |
| return reflect.Value{}, err | |
| } | |
| } | |
| } | |
| if !parent.IsNil() { | |
| return cldr.inheritFields(v.Elem(), parent.Elem()) | |
| } | |
| } else if parent.IsNil() { | |
| panic("should not reach here") | |
| } | |
| return v, nil | |
| } | |
| // Must be slice of struct pointers. | |
| func (cldr *CLDR) inheritSlice(enc, v, parent reflect.Value) (res reflect.Value, err error) { | |
| t := v.Type() | |
| index := make(map[string]reflect.Value) | |
| if !v.IsNil() { | |
| for i := 0; i < v.Len(); i++ { | |
| vi := v.Index(i) | |
| key := attrKey(vi) | |
| index[key] = vi | |
| } | |
| } | |
| if !parent.IsNil() { | |
| for i := 0; i < parent.Len(); i++ { | |
| vi := parent.Index(i) | |
| key := attrKey(vi) | |
| if w, ok := index[key]; ok { | |
| index[key], err = cldr.inheritStructPtr(w, vi) | |
| } else { | |
| n := cldr.newNode(vi.Elem(), enc) | |
| index[key], err = cldr.inheritStructPtr(n, vi) | |
| } | |
| index[key].Interface().(Elem).setEnclosing(enc.Addr().Interface().(Elem)) | |
| if err != nil { | |
| return v, err | |
| } | |
| } | |
| } | |
| keys := make([]string, 0, len(index)) | |
| for k, _ := range index { | |
| keys = append(keys, k) | |
| } | |
| sort.Strings(keys) | |
| sl := reflect.MakeSlice(t, len(index), len(index)) | |
| for i, k := range keys { | |
| sl.Index(i).Set(index[k]) | |
| } | |
| return sl, nil | |
| } | |
| func parentLocale(loc string) string { | |
| parts := strings.Split(loc, "_") | |
| if len(parts) == 1 { | |
| return "root" | |
| } | |
| parts = parts[:len(parts)-1] | |
| key := strings.Join(parts, "_") | |
| return key | |
| } | |
| func (cldr *CLDR) resolve(loc string) (res *LDML, err error) { | |
| if r := cldr.resolved[loc]; r != nil { | |
| return r, nil | |
| } | |
| x := cldr.RawLDML(loc) | |
| if x == nil { | |
| return nil, fmt.Errorf("cldr: unknown locale %q", loc) | |
| } | |
| var v reflect.Value | |
| if loc == "root" { | |
| x = deepCopy(reflect.ValueOf(x)).Interface().(*LDML) | |
| linkEnclosing(nil, x) | |
| err = cldr.aliasResolver().visit(x) | |
| } else { | |
| key := parentLocale(loc) | |
| var parent *LDML | |
| for ; cldr.locale[key] == nil; key = parentLocale(key) { | |
| } | |
| if parent, err = cldr.resolve(key); err != nil { | |
| return nil, err | |
| } | |
| v, err = cldr.inheritFields(reflect.ValueOf(x).Elem(), reflect.ValueOf(parent).Elem()) | |
| x = v.Interface().(*LDML) | |
| linkEnclosing(nil, x) | |
| } | |
| if err != nil { | |
| return nil, err | |
| } | |
| cldr.resolved[loc] = x | |
| return x, err | |
| } | |
| // finalize finalizes the initialization of the raw LDML structs. It also | |
| // removed unwanted fields, as specified by filter, so that they will not | |
| // be unnecessarily evaluated. | |
| func (cldr *CLDR) finalize(filter []string) { | |
| for _, x := range cldr.locale { | |
| if filter != nil { | |
| v := reflect.ValueOf(x).Elem() | |
| t := v.Type() | |
| for i := 0; i < v.NumField(); i++ { | |
| f := t.Field(i) | |
| name, _ := xmlName(f) | |
| if name != "" && name != "identity" && !in(filter, name) { | |
| v.Field(i).Set(reflect.Zero(f.Type)) | |
| } | |
| } | |
| } | |
| linkEnclosing(nil, x) // for resolving aliases and paths | |
| setNames(x, "ldml") | |
| } | |
| } |