aboutsummaryrefslogblamecommitdiffstats
path: root/vendor/golang.org/x/text/unicode/cldr/decode.go
blob: e5ee4aed184b815563415385ffb48b3dcc6f9d8e (plain) (tree)










































































































































































                                                                                                    
// 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})
}