| // Copyright 2017 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 cldrtree |
| |
| import ( |
| "log" |
| "strconv" |
| ) |
| |
| // enumIndex is the numerical value of an enum value. |
| type enumIndex int |
| |
| // An enum is a collection of enum values. |
| type enum struct { |
| name string // the Go type of the enum |
| rename func(string) string |
| keyMap map[string]enumIndex |
| keys []string |
| } |
| |
| // lookup returns the index for the enum corresponding to the string. If s |
| // currently does not exist it will add the entry. |
| func (e *enum) lookup(s string) enumIndex { |
| if e.rename != nil { |
| s = e.rename(s) |
| } |
| x, ok := e.keyMap[s] |
| if !ok { |
| if e.keyMap == nil { |
| e.keyMap = map[string]enumIndex{} |
| } |
| u, err := strconv.ParseUint(s, 10, 32) |
| if err == nil { |
| for len(e.keys) <= int(u) { |
| x := enumIndex(len(e.keys)) |
| s := strconv.Itoa(int(x)) |
| e.keyMap[s] = x |
| e.keys = append(e.keys, s) |
| } |
| if e.keyMap[s] != enumIndex(u) { |
| // TODO: handle more gracefully. |
| log.Fatalf("cldrtree: mix of integer and non-integer for %q %v", s, e.keys) |
| } |
| return enumIndex(u) |
| } |
| x = enumIndex(len(e.keys)) |
| e.keyMap[s] = x |
| e.keys = append(e.keys, s) |
| } |
| return x |
| } |
| |
| // A typeInfo indicates the set of possible enum values and a mapping from |
| // these values to subtypes. |
| type typeInfo struct { |
| enum *enum |
| entries map[enumIndex]*typeInfo |
| keyTypeInfo *typeInfo |
| shareKeys bool |
| } |
| |
| func (t *typeInfo) sharedKeys() bool { |
| return t.shareKeys |
| } |
| |
| func (t *typeInfo) lookupSubtype(s string, opts *options) (x enumIndex, sub *typeInfo) { |
| if t.enum == nil { |
| if t.enum = opts.sharedEnums; t.enum == nil { |
| t.enum = &enum{} |
| } |
| } |
| if opts.sharedEnums != nil && t.enum != opts.sharedEnums { |
| panic("incompatible enums defined") |
| } |
| x = t.enum.lookup(s) |
| if t.entries == nil { |
| t.entries = map[enumIndex]*typeInfo{} |
| } |
| sub, ok := t.entries[x] |
| if !ok { |
| sub = opts.sharedType |
| if sub == nil { |
| sub = &typeInfo{} |
| } |
| t.entries[x] = sub |
| } |
| t.shareKeys = opts.sharedType != nil // For analysis purposes. |
| return x, sub |
| } |
| |
| // metaData includes information about subtypes, possibly sharing commonality |
| // with sibling branches, and information about inheritance, which may differ |
| // per branch. |
| type metaData struct { |
| b *Builder |
| |
| parent *metaData |
| |
| index enumIndex // index into the parent's subtype index |
| key string |
| elem string // XML element corresponding to this type. |
| typeInfo *typeInfo |
| |
| lookup map[enumIndex]*metaData |
| subs []*metaData |
| |
| inheritOffset int // always negative when applicable |
| inheritIndex string // new value for field indicated by inheritOffset |
| // inheritType *metaData |
| } |
| |
| func (m *metaData) sub(key string, opts *options) *metaData { |
| if m.lookup == nil { |
| m.lookup = map[enumIndex]*metaData{} |
| } |
| enum, info := m.typeInfo.lookupSubtype(key, opts) |
| sub := m.lookup[enum] |
| if sub == nil { |
| sub = &metaData{ |
| b: m.b, |
| parent: m, |
| |
| index: enum, |
| key: key, |
| typeInfo: info, |
| } |
| m.lookup[enum] = sub |
| m.subs = append(m.subs, sub) |
| } |
| return sub |
| } |
| |
| func (m *metaData) validate() { |
| for _, s := range m.subs { |
| s.validate() |
| } |
| } |