summaryrefslogtreecommitdiffhomepage
path: root/vendor/golang.org/x/text/unicode/cldr/decode.go
blob: 7a8fb5ae70624d9eb2b49667e4d12b7ece048411 (plain) (blame)
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
// 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

import (
	"archive/zip"
	"bytes"
	"encoding/xml"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"regexp"
)

// A Decoder loads an archive of CLDR data.
type Decoder struct {
	dirFilter     []string
	sectionFilter []string
	loader        Loader
	cldr          *CLDR
	curLocale     string
}

// SetSectionFilter takes a list top-level LDML element names to which
// evaluation of LDML should be limited.  It automatically calls SetDirFilter.
func (d *Decoder) SetSectionFilter(filter ...string) {
	d.sectionFilter = filter
	// TODO: automatically set dir filter
}

// SetDirFilter limits the loading of LDML XML files of the specified directories.
// Note that sections may be split across directories differently for different CLDR versions.
// For more robust code, use SetSectionFilter.
func (d *Decoder) SetDirFilter(dir ...string) {
	d.dirFilter = dir
}

// A Loader provides access to the files of a CLDR archive.
type Loader interface {
	Len() int
	Path(i int) string
	Reader(i int) (io.ReadCloser, error)
}

var fileRe = regexp.MustCompile(`.*[/\\](.*)[/\\](.*)\.xml`)

// Decode loads and decodes the files represented by l.
func (d *Decoder) Decode(l Loader) (cldr *CLDR, err error) {
	d.cldr = makeCLDR()
	for i := 0; i < l.Len(); i++ {
		fname := l.Path(i)
		if m := fileRe.FindStringSubmatch(fname); m != nil {
			if len(d.dirFilter) > 0 && !in(d.dirFilter, m[1]) {
				continue
			}
			var r io.ReadCloser
			if r, err = l.Reader(i); err == nil {
				err = d.decode(m[1], m[2], r)
				r.Close()
			}
			if err != nil {
				return nil, err
			}
		}
	}
	d.cldr.finalize(d.sectionFilter)
	return d.cldr, nil
}

func (d *Decoder) decode(dir, id string, r io.Reader) error {
	var v interface{}
	var l *LDML
	cldr := d.cldr
	switch {
	case dir == "supplemental":
		v = cldr.supp
	case dir == "transforms":
		return nil
	case dir == "bcp47":
		v = cldr.bcp47
	case dir == "validity":
		return nil
	default:
		ok := false
		if v, ok = cldr.locale[id]; !ok {
			l = &LDML{}
			v, cldr.locale[id] = l, l
		}
	}
	x := xml.NewDecoder(r)
	if err := x.Decode(v); err != nil {
		log.Printf("%s/%s: %v", dir, id, err)
		return err
	}
	if l != nil {
		if l.Identity == nil {
			return fmt.Errorf("%s/%s: missing identity element", dir, id)
		}
		// TODO: verify when CLDR bug https://unicode.org/cldr/trac/ticket/8970
		// is resolved.
		// path := strings.Split(id, "_")
		// if lang := l.Identity.Language.Type; lang != path[0] {
		// 	return fmt.Errorf("%s/%s: language was %s; want %s", dir, id, lang, path[0])
		// }
	}
	return nil
}

type pathLoader []string

func makePathLoader(path string) (pl pathLoader, err error) {
	err = filepath.Walk(path, func(path string, _ os.FileInfo, err error) error {
		pl = append(pl, path)
		return err
	})
	return pl, err
}

func (pl pathLoader) Len() int {
	return len(pl)
}

func (pl pathLoader) Path(i int) string {
	return pl[i]
}

func (pl pathLoader) Reader(i int) (io.ReadCloser, error) {
	return os.Open(pl[i])
}

// DecodePath loads CLDR data from the given path.
func (d *Decoder) DecodePath(path string) (cldr *CLDR, err error) {
	loader, err := makePathLoader(path)
	if err != nil {
		return nil, err
	}
	return d.Decode(loader)
}

type zipLoader struct {
	r *zip.Reader
}

func (zl zipLoader) Len() int {
	return len(zl.r.File)
}

func (zl zipLoader) Path(i int) string {
	return zl.r.File[i].Name
}

func (zl zipLoader) Reader(i int) (io.ReadCloser, error) {
	return zl.r.File[i].Open()
}

// DecodeZip loads CLDR data from the zip archive for which r is the source.
func (d *Decoder) DecodeZip(r io.Reader) (cldr *CLDR, err error) {
	buffer, err := io.ReadAll(r)
	if err != nil {
		return nil, err
	}
	archive, err := zip.NewReader(bytes.NewReader(buffer), int64(len(buffer)))
	if err != nil {
		return nil, err
	}
	return d.Decode(zipLoader{archive})
}