| // 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 ( | |
| "fmt" | |
| "reflect" | |
| "sort" | |
| ) | |
| // Slice provides utilities for modifying slices of elements. | |
| // It can be wrapped around any slice of which the element type implements | |
| // interface Elem. | |
| type Slice struct { | |
| ptr reflect.Value | |
| typ reflect.Type | |
| } | |
| // Value returns the reflect.Value of the underlying slice. | |
| func (s *Slice) Value() reflect.Value { | |
| return s.ptr.Elem() | |
| } | |
| // MakeSlice wraps a pointer to a slice of Elems. | |
| // It replaces the array pointed to by the slice so that subsequent modifications | |
| // do not alter the data in a CLDR type. | |
| // It panics if an incorrect type is passed. | |
| func MakeSlice(slicePtr interface{}) Slice { | |
| ptr := reflect.ValueOf(slicePtr) | |
| if ptr.Kind() != reflect.Ptr { | |
| panic(fmt.Sprintf("MakeSlice: argument must be pointer to slice, found %v", ptr.Type())) | |
| } | |
| sl := ptr.Elem() | |
| if sl.Kind() != reflect.Slice { | |
| panic(fmt.Sprintf("MakeSlice: argument must point to a slice, found %v", sl.Type())) | |
| } | |
| intf := reflect.TypeFor[Elem]() | |
| if !sl.Type().Elem().Implements(intf) { | |
| panic(fmt.Sprintf("MakeSlice: element type of slice (%v) does not implement Elem", sl.Type().Elem())) | |
| } | |
| nsl := reflect.MakeSlice(sl.Type(), sl.Len(), sl.Len()) | |
| reflect.Copy(nsl, sl) | |
| sl.Set(nsl) | |
| return Slice{ | |
| ptr: ptr, | |
| typ: sl.Type().Elem().Elem(), | |
| } | |
| } | |
| func (s Slice) indexForAttr(a string) []int { | |
| for i := iter(reflect.Zero(s.typ)); !i.done(); i.next() { | |
| if n, _ := xmlName(i.field()); n == a { | |
| return i.index | |
| } | |
| } | |
| panic(fmt.Sprintf("MakeSlice: no attribute %q for type %v", a, s.typ)) | |
| } | |
| // Filter filters s to only include elements for which fn returns true. | |
| func (s Slice) Filter(fn func(e Elem) bool) { | |
| k := 0 | |
| sl := s.Value() | |
| for i := 0; i < sl.Len(); i++ { | |
| vi := sl.Index(i) | |
| if fn(vi.Interface().(Elem)) { | |
| sl.Index(k).Set(vi) | |
| k++ | |
| } | |
| } | |
| sl.Set(sl.Slice(0, k)) | |
| } | |
| // Group finds elements in s for which fn returns the same value and groups | |
| // them in a new Slice. | |
| func (s Slice) Group(fn func(e Elem) string) []Slice { | |
| m := make(map[string][]reflect.Value) | |
| sl := s.Value() | |
| for i := 0; i < sl.Len(); i++ { | |
| vi := sl.Index(i) | |
| key := fn(vi.Interface().(Elem)) | |
| m[key] = append(m[key], vi) | |
| } | |
| keys := []string{} | |
| for k, _ := range m { | |
| keys = append(keys, k) | |
| } | |
| sort.Strings(keys) | |
| res := []Slice{} | |
| for _, k := range keys { | |
| nsl := reflect.New(sl.Type()) | |
| nsl.Elem().Set(reflect.Append(nsl.Elem(), m[k]...)) | |
| res = append(res, MakeSlice(nsl.Interface())) | |
| } | |
| return res | |
| } | |
| // SelectAnyOf filters s to contain only elements for which attr matches | |
| // any of the values. | |
| func (s Slice) SelectAnyOf(attr string, values ...string) { | |
| index := s.indexForAttr(attr) | |
| s.Filter(func(e Elem) bool { | |
| vf := reflect.ValueOf(e).Elem().FieldByIndex(index) | |
| return in(values, vf.String()) | |
| }) | |
| } | |
| // SelectOnePerGroup filters s to include at most one element e per group of | |
| // elements matching Key(attr), where e has an attribute a that matches any | |
| // the values in v. | |
| // If more than one element in a group matches a value in v preference | |
| // is given to the element that matches the first value in v. | |
| func (s Slice) SelectOnePerGroup(a string, v []string) { | |
| index := s.indexForAttr(a) | |
| grouped := s.Group(func(e Elem) string { return Key(e, a) }) | |
| sl := s.Value() | |
| sl.Set(sl.Slice(0, 0)) | |
| for _, g := range grouped { | |
| e := reflect.Value{} | |
| found := len(v) | |
| gsl := g.Value() | |
| for i := 0; i < gsl.Len(); i++ { | |
| vi := gsl.Index(i).Elem().FieldByIndex(index) | |
| j := 0 | |
| for ; j < len(v) && v[j] != vi.String(); j++ { | |
| } | |
| if j < found { | |
| found = j | |
| e = gsl.Index(i) | |
| } | |
| } | |
| if found < len(v) { | |
| sl.Set(reflect.Append(sl, e)) | |
| } | |
| } | |
| } | |
| // SelectDraft drops all elements from the list with a draft level smaller than d | |
| // and selects the highest draft level of the remaining. | |
| // This method assumes that the input CLDR is canonicalized. | |
| func (s Slice) SelectDraft(d Draft) { | |
| s.SelectOnePerGroup("draft", drafts[len(drafts)-2-int(d):]) | |
| } |