1// Copyright 2017 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 cldrtree
6
7import (
8	"log"
9	"strconv"
10)
11
12// enumIndex is the numerical value of an enum value.
13type enumIndex int
14
15// An enum is a collection of enum values.
16type enum struct {
17	name   string // the Go type of the enum
18	rename func(string) string
19	keyMap map[string]enumIndex
20	keys   []string
21}
22
23// lookup returns the index for the enum corresponding to the string. If s
24// currently does not exist it will add the entry.
25func (e *enum) lookup(s string) enumIndex {
26	if e.rename != nil {
27		s = e.rename(s)
28	}
29	x, ok := e.keyMap[s]
30	if !ok {
31		if e.keyMap == nil {
32			e.keyMap = map[string]enumIndex{}
33		}
34		u, err := strconv.ParseUint(s, 10, 32)
35		if err == nil {
36			for len(e.keys) <= int(u) {
37				x := enumIndex(len(e.keys))
38				s := strconv.Itoa(int(x))
39				e.keyMap[s] = x
40				e.keys = append(e.keys, s)
41			}
42			if e.keyMap[s] != enumIndex(u) {
43				// TODO: handle more gracefully.
44				log.Fatalf("cldrtree: mix of integer and non-integer for %q %v", s, e.keys)
45			}
46			return enumIndex(u)
47		}
48		x = enumIndex(len(e.keys))
49		e.keyMap[s] = x
50		e.keys = append(e.keys, s)
51	}
52	return x
53}
54
55// A typeInfo indicates the set of possible enum values and a mapping from
56// these values to subtypes.
57type typeInfo struct {
58	enum        *enum
59	entries     map[enumIndex]*typeInfo
60	keyTypeInfo *typeInfo
61	shareKeys   bool
62}
63
64func (t *typeInfo) sharedKeys() bool {
65	return t.shareKeys
66}
67
68func (t *typeInfo) lookupSubtype(s string, opts *options) (x enumIndex, sub *typeInfo) {
69	if t.enum == nil {
70		if t.enum = opts.sharedEnums; t.enum == nil {
71			t.enum = &enum{}
72		}
73	}
74	if opts.sharedEnums != nil && t.enum != opts.sharedEnums {
75		panic("incompatible enums defined")
76	}
77	x = t.enum.lookup(s)
78	if t.entries == nil {
79		t.entries = map[enumIndex]*typeInfo{}
80	}
81	sub, ok := t.entries[x]
82	if !ok {
83		sub = opts.sharedType
84		if sub == nil {
85			sub = &typeInfo{}
86		}
87		t.entries[x] = sub
88	}
89	t.shareKeys = opts.sharedType != nil // For analysis purposes.
90	return x, sub
91}
92
93// metaData includes information about subtypes, possibly sharing commonality
94// with sibling branches, and information about inheritance, which may differ
95// per branch.
96type metaData struct {
97	b *Builder
98
99	parent *metaData
100
101	index    enumIndex // index into the parent's subtype index
102	key      string
103	elem     string // XML element corresponding to this type.
104	typeInfo *typeInfo
105
106	lookup map[enumIndex]*metaData
107	subs   []*metaData
108
109	inheritOffset int    // always negative when applicable
110	inheritIndex  string // new value for field indicated by inheritOffset
111	// inheritType   *metaData
112}
113
114func (m *metaData) sub(key string, opts *options) *metaData {
115	if m.lookup == nil {
116		m.lookup = map[enumIndex]*metaData{}
117	}
118	enum, info := m.typeInfo.lookupSubtype(key, opts)
119	sub := m.lookup[enum]
120	if sub == nil {
121		sub = &metaData{
122			b:      m.b,
123			parent: m,
124
125			index:    enum,
126			key:      key,
127			typeInfo: info,
128		}
129		m.lookup[enum] = sub
130		m.subs = append(m.subs, sub)
131	}
132	return sub
133}
134
135func (m *metaData) validate() {
136	for _, s := range m.subs {
137		s.validate()
138	}
139}
140