123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171 |
- // 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"
- "io/ioutil"
- "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 specied 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.Reader
- if r, err = l.Reader(i); err == nil {
- err = d.decode(m[1], m[2], r)
- }
- 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 http://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 := ioutil.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})
- }
|