123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185 |
- package plist
- import (
- "encoding"
- "reflect"
- "time"
- )
- func isEmptyValue(v reflect.Value) bool {
- switch v.Kind() {
- case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
- return v.Len() == 0
- case reflect.Bool:
- return !v.Bool()
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return v.Int() == 0
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- return v.Uint() == 0
- case reflect.Float32, reflect.Float64:
- return v.Float() == 0
- case reflect.Interface, reflect.Ptr:
- return v.IsNil()
- }
- return false
- }
- var (
- plistMarshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem()
- textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
- timeType = reflect.TypeOf((*time.Time)(nil)).Elem()
- )
- func implementsInterface(val reflect.Value, interfaceType reflect.Type) (interface{}, bool) {
- if val.CanInterface() && val.Type().Implements(interfaceType) {
- return val.Interface(), true
- }
- if val.CanAddr() {
- pv := val.Addr()
- if pv.CanInterface() && pv.Type().Implements(interfaceType) {
- return pv.Interface(), true
- }
- }
- return nil, false
- }
- func (p *Encoder) marshalPlistInterface(marshalable Marshaler) cfValue {
- value, err := marshalable.MarshalPlist()
- if err != nil {
- panic(err)
- }
- return p.marshal(reflect.ValueOf(value))
- }
- // marshalTextInterface marshals a TextMarshaler to a plist string.
- func (p *Encoder) marshalTextInterface(marshalable encoding.TextMarshaler) cfValue {
- s, err := marshalable.MarshalText()
- if err != nil {
- panic(err)
- }
- return cfString(s)
- }
- // marshalStruct marshals a reflected struct value to a plist dictionary
- func (p *Encoder) marshalStruct(typ reflect.Type, val reflect.Value) cfValue {
- tinfo, _ := getTypeInfo(typ)
- dict := &cfDictionary{
- keys: make([]string, 0, len(tinfo.fields)),
- values: make([]cfValue, 0, len(tinfo.fields)),
- }
- for _, finfo := range tinfo.fields {
- value := finfo.value(val)
- if !value.IsValid() || finfo.omitEmpty && isEmptyValue(value) {
- continue
- }
- dict.keys = append(dict.keys, finfo.name)
- dict.values = append(dict.values, p.marshal(value))
- }
- return dict
- }
- func (p *Encoder) marshalTime(val reflect.Value) cfValue {
- time := val.Interface().(time.Time)
- return cfDate(time)
- }
- func (p *Encoder) marshal(val reflect.Value) cfValue {
- if !val.IsValid() {
- return nil
- }
- if receiver, can := implementsInterface(val, plistMarshalerType); can {
- return p.marshalPlistInterface(receiver.(Marshaler))
- }
- // time.Time implements TextMarshaler, but we need to store it in RFC3339
- if val.Type() == timeType {
- return p.marshalTime(val)
- }
- if val.Kind() == reflect.Ptr || (val.Kind() == reflect.Interface && val.NumMethod() == 0) {
- ival := val.Elem()
- if ival.IsValid() && ival.Type() == timeType {
- return p.marshalTime(ival)
- }
- }
- // Check for text marshaler.
- if receiver, can := implementsInterface(val, textMarshalerType); can {
- return p.marshalTextInterface(receiver.(encoding.TextMarshaler))
- }
- // Descend into pointers or interfaces
- if val.Kind() == reflect.Ptr || (val.Kind() == reflect.Interface && val.NumMethod() == 0) {
- val = val.Elem()
- }
- // We got this far and still may have an invalid anything or nil ptr/interface
- if !val.IsValid() || ((val.Kind() == reflect.Ptr || val.Kind() == reflect.Interface) && val.IsNil()) {
- return nil
- }
- typ := val.Type()
- if typ == uidType {
- return cfUID(val.Uint())
- }
- if val.Kind() == reflect.Struct {
- return p.marshalStruct(typ, val)
- }
- switch val.Kind() {
- case reflect.String:
- return cfString(val.String())
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return &cfNumber{signed: true, value: uint64(val.Int())}
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- return &cfNumber{signed: false, value: val.Uint()}
- case reflect.Float32:
- return &cfReal{wide: false, value: val.Float()}
- case reflect.Float64:
- return &cfReal{wide: true, value: val.Float()}
- case reflect.Bool:
- return cfBoolean(val.Bool())
- case reflect.Slice, reflect.Array:
- if typ.Elem().Kind() == reflect.Uint8 {
- bytes := []byte(nil)
- if val.CanAddr() {
- bytes = val.Bytes()
- } else {
- bytes = make([]byte, val.Len())
- reflect.Copy(reflect.ValueOf(bytes), val)
- }
- return cfData(bytes)
- }
- values := make([]cfValue, val.Len())
- for i, length := 0, val.Len(); i < length; i++ {
- if subpval := p.marshal(val.Index(i)); subpval != nil {
- values[i] = subpval
- }
- }
- return &cfArray{values}
- case reflect.Map:
- if typ.Key().Kind() != reflect.String {
- panic(&unknownTypeError{typ})
- }
- l := val.Len()
- dict := &cfDictionary{
- keys: make([]string, 0, l),
- values: make([]cfValue, 0, l),
- }
- for _, keyv := range val.MapKeys() {
- if subpval := p.marshal(val.MapIndex(keyv)); subpval != nil {
- dict.keys = append(dict.keys, keyv.String())
- dict.values = append(dict.values, subpval)
- }
- }
- return dict
- default:
- panic(&unknownTypeError{typ})
- }
- }
|