You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
144 lines
4.0 KiB
144 lines
4.0 KiB
// 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.TypeOf((*Elem)(nil)).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):]) |
|
}
|
|
|