github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/text/cldr/decode.go (about) 1 // Copyright 2013 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 5 package cldr 6 7 import ( 8 "archive/zip" 9 "bytes" 10 "encoding/xml" 11 "fmt" 12 "io" 13 "io/ioutil" 14 "log" 15 "os" 16 "path/filepath" 17 "regexp" 18 "strings" 19 ) 20 21 // A Decoder loads an archive of CLDR data. 22 type Decoder struct { 23 dirFilter []string 24 sectionFilter []string 25 loader Loader 26 cldr *CLDR 27 curLocale string 28 } 29 30 // SetSectionFilter takes a list top-level LDML element names to which 31 // evaluation of LDML should be limited. It automatically calls SetDirFilter. 32 func (d *Decoder) SetSectionFilter(filter ...string) { 33 d.sectionFilter = filter 34 // TODO: automatically set dir filter 35 } 36 37 // SetDirFilter limits the loading of LDML XML files of the specied directories. 38 // Note that sections may be split across directories differently for different CLDR versions. 39 // For more robust code, use SetSectionFilter. 40 func (d *Decoder) SetDirFilter(dir ...string) { 41 d.dirFilter = dir 42 } 43 44 // A Loader provides access to the files of a CLDR archive. 45 type Loader interface { 46 Len() int 47 Path(i int) string 48 Reader(i int) (io.ReadCloser, error) 49 } 50 51 var fileRe = regexp.MustCompile(".*/(.*)/(.*)\\.xml") 52 53 // Decode loads and decodes the files represented by l. 54 func (d *Decoder) Decode(l Loader) (cldr *CLDR, err error) { 55 d.cldr = makeCLDR() 56 for i := 0; i < l.Len(); i++ { 57 fname := l.Path(i) 58 if m := fileRe.FindStringSubmatch(fname); m != nil { 59 if len(d.dirFilter) > 0 && !in(d.dirFilter, m[1]) { 60 continue 61 } 62 var r io.Reader 63 if r, err = l.Reader(i); err == nil { 64 err = d.decode(m[1], m[2], r) 65 } 66 if err != nil { 67 return nil, err 68 } 69 } 70 } 71 d.cldr.finalize(d.sectionFilter) 72 return d.cldr, nil 73 } 74 75 func (d *Decoder) decode(dir, id string, r io.Reader) error { 76 var v interface{} 77 var l *LDML 78 cldr := d.cldr 79 switch { 80 case dir == "supplemental": 81 v = cldr.supp 82 case dir == "transforms": 83 return nil 84 case dir == "bcp47": 85 v = cldr.bcp47 86 default: 87 ok := false 88 if v, ok = cldr.locale[id]; !ok { 89 l = &LDML{} 90 v, cldr.locale[id] = l, l 91 } 92 } 93 x := xml.NewDecoder(r) 94 if err := x.Decode(v); err != nil { 95 log.Printf("%s/%s: %v", dir, id, err) 96 return err 97 } 98 if l != nil { 99 if l.Identity == nil { 100 return fmt.Errorf("%s/%s: missing identity element", dir, id) 101 } 102 // TODO: use Locale.Parse 103 path := strings.Split(id, "_") 104 if lang := l.Identity.Language.Type; lang != path[0] { 105 return fmt.Errorf("%s/%s: language was %s; want %s", dir, id, lang, path[0]) 106 } 107 } 108 return nil 109 } 110 111 type pathLoader []string 112 113 func makePathLoader(path string) (pl pathLoader, err error) { 114 err = filepath.Walk(path, func(path string, _ os.FileInfo, err error) error { 115 pl = append(pl, path) 116 return err 117 }) 118 return pl, err 119 } 120 121 func (pl pathLoader) Len() int { 122 return len(pl) 123 } 124 125 func (pl pathLoader) Path(i int) string { 126 return pl[i] 127 } 128 129 func (pl pathLoader) Reader(i int) (io.ReadCloser, error) { 130 return os.Open(pl[i]) 131 } 132 133 // DecodePath loads CLDR data from the given path. 134 func (d *Decoder) DecodePath(path string) (cldr *CLDR, err error) { 135 loader, err := makePathLoader(path) 136 if err != nil { 137 return nil, err 138 } 139 return d.Decode(loader) 140 } 141 142 type zipLoader struct { 143 r *zip.Reader 144 } 145 146 func (zl zipLoader) Len() int { 147 return len(zl.r.File) 148 } 149 150 func (zl zipLoader) Path(i int) string { 151 return zl.r.File[i].Name 152 } 153 154 func (zl zipLoader) Reader(i int) (io.ReadCloser, error) { 155 return zl.r.File[i].Open() 156 } 157 158 // DecodeZip loads CLDR data from the zip archive for which r is the source. 159 func (d *Decoder) DecodeZip(r io.Reader) (cldr *CLDR, err error) { 160 buffer, err := ioutil.ReadAll(r) 161 if err != nil { 162 return nil, err 163 } 164 archive, err := zip.NewReader(bytes.NewReader(buffer), int64(len(buffer))) 165 if err != nil { 166 return nil, err 167 } 168 return d.Decode(zipLoader{archive}) 169 }