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