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  }