blob: 388c983ff133a648ddc0590b07e9a0c8d285474c [file] [log] [blame]
Don Newton98fd8812019-09-23 15:15:02 -04001// 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
5package cldr
6
7import (
8 "fmt"
9 "reflect"
10 "sort"
11)
12
13// Slice provides utilities for modifying slices of elements.
14// It can be wrapped around any slice of which the element type implements
15// interface Elem.
16type Slice struct {
17 ptr reflect.Value
18 typ reflect.Type
19}
20
21// Value returns the reflect.Value of the underlying slice.
22func (s *Slice) Value() reflect.Value {
23 return s.ptr.Elem()
24}
25
26// MakeSlice wraps a pointer to a slice of Elems.
27// It replaces the array pointed to by the slice so that subsequent modifications
28// do not alter the data in a CLDR type.
29// It panics if an incorrect type is passed.
30func MakeSlice(slicePtr interface{}) Slice {
31 ptr := reflect.ValueOf(slicePtr)
32 if ptr.Kind() != reflect.Ptr {
33 panic(fmt.Sprintf("MakeSlice: argument must be pointer to slice, found %v", ptr.Type()))
34 }
35 sl := ptr.Elem()
36 if sl.Kind() != reflect.Slice {
37 panic(fmt.Sprintf("MakeSlice: argument must point to a slice, found %v", sl.Type()))
38 }
39 intf := reflect.TypeOf((*Elem)(nil)).Elem()
40 if !sl.Type().Elem().Implements(intf) {
41 panic(fmt.Sprintf("MakeSlice: element type of slice (%v) does not implement Elem", sl.Type().Elem()))
42 }
43 nsl := reflect.MakeSlice(sl.Type(), sl.Len(), sl.Len())
44 reflect.Copy(nsl, sl)
45 sl.Set(nsl)
46 return Slice{
47 ptr: ptr,
48 typ: sl.Type().Elem().Elem(),
49 }
50}
51
52func (s Slice) indexForAttr(a string) []int {
53 for i := iter(reflect.Zero(s.typ)); !i.done(); i.next() {
54 if n, _ := xmlName(i.field()); n == a {
55 return i.index
56 }
57 }
58 panic(fmt.Sprintf("MakeSlice: no attribute %q for type %v", a, s.typ))
59}
60
61// Filter filters s to only include elements for which fn returns true.
62func (s Slice) Filter(fn func(e Elem) bool) {
63 k := 0
64 sl := s.Value()
65 for i := 0; i < sl.Len(); i++ {
66 vi := sl.Index(i)
67 if fn(vi.Interface().(Elem)) {
68 sl.Index(k).Set(vi)
69 k++
70 }
71 }
72 sl.Set(sl.Slice(0, k))
73}
74
75// Group finds elements in s for which fn returns the same value and groups
76// them in a new Slice.
77func (s Slice) Group(fn func(e Elem) string) []Slice {
78 m := make(map[string][]reflect.Value)
79 sl := s.Value()
80 for i := 0; i < sl.Len(); i++ {
81 vi := sl.Index(i)
82 key := fn(vi.Interface().(Elem))
83 m[key] = append(m[key], vi)
84 }
85 keys := []string{}
86 for k, _ := range m {
87 keys = append(keys, k)
88 }
89 sort.Strings(keys)
90 res := []Slice{}
91 for _, k := range keys {
92 nsl := reflect.New(sl.Type())
93 nsl.Elem().Set(reflect.Append(nsl.Elem(), m[k]...))
94 res = append(res, MakeSlice(nsl.Interface()))
95 }
96 return res
97}
98
99// SelectAnyOf filters s to contain only elements for which attr matches
100// any of the values.
101func (s Slice) SelectAnyOf(attr string, values ...string) {
102 index := s.indexForAttr(attr)
103 s.Filter(func(e Elem) bool {
104 vf := reflect.ValueOf(e).Elem().FieldByIndex(index)
105 return in(values, vf.String())
106 })
107}
108
109// SelectOnePerGroup filters s to include at most one element e per group of
110// elements matching Key(attr), where e has an attribute a that matches any
111// the values in v.
112// If more than one element in a group matches a value in v preference
113// is given to the element that matches the first value in v.
114func (s Slice) SelectOnePerGroup(a string, v []string) {
115 index := s.indexForAttr(a)
116 grouped := s.Group(func(e Elem) string { return Key(e, a) })
117 sl := s.Value()
118 sl.Set(sl.Slice(0, 0))
119 for _, g := range grouped {
120 e := reflect.Value{}
121 found := len(v)
122 gsl := g.Value()
123 for i := 0; i < gsl.Len(); i++ {
124 vi := gsl.Index(i).Elem().FieldByIndex(index)
125 j := 0
126 for ; j < len(v) && v[j] != vi.String(); j++ {
127 }
128 if j < found {
129 found = j
130 e = gsl.Index(i)
131 }
132 }
133 if found < len(v) {
134 sl.Set(reflect.Append(sl, e))
135 }
136 }
137}
138
139// SelectDraft drops all elements from the list with a draft level smaller than d
140// and selects the highest draft level of the remaining.
141// This method assumes that the input CLDR is canonicalized.
142func (s Slice) SelectDraft(d Draft) {
143 s.SelectOnePerGroup("draft", drafts[len(drafts)-2-int(d):])
144}