123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315 |
- package plist
- import (
- "encoding"
- "fmt"
- "reflect"
- "runtime"
- "time"
- )
- type incompatibleDecodeTypeError struct {
- dest reflect.Type
- src string // type name (from cfValue)
- }
- func (u *incompatibleDecodeTypeError) Error() string {
- return fmt.Sprintf("plist: type mismatch: tried to decode plist type `%v' into value of type `%v'", u.src, u.dest)
- }
- var (
- plistUnmarshalerType = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
- textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
- uidType = reflect.TypeOf(UID(0))
- )
- func isEmptyInterface(v reflect.Value) bool {
- return v.Kind() == reflect.Interface && v.NumMethod() == 0
- }
- func (p *Decoder) unmarshalPlistInterface(pval cfValue, unmarshalable Unmarshaler) {
- err := unmarshalable.UnmarshalPlist(func(i interface{}) (err error) {
- defer func() {
- if r := recover(); r != nil {
- if _, ok := r.(runtime.Error); ok {
- panic(r)
- }
- err = r.(error)
- }
- }()
- p.unmarshal(pval, reflect.ValueOf(i))
- return
- })
- if err != nil {
- panic(err)
- }
- }
- func (p *Decoder) unmarshalTextInterface(pval cfString, unmarshalable encoding.TextUnmarshaler) {
- err := unmarshalable.UnmarshalText([]byte(pval))
- if err != nil {
- panic(err)
- }
- }
- func (p *Decoder) unmarshalTime(pval cfDate, val reflect.Value) {
- val.Set(reflect.ValueOf(time.Time(pval)))
- }
- func (p *Decoder) unmarshalLaxString(s string, val reflect.Value) {
- switch val.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- i := mustParseInt(s, 10, 64)
- val.SetInt(i)
- return
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- i := mustParseUint(s, 10, 64)
- val.SetUint(i)
- return
- case reflect.Float32, reflect.Float64:
- f := mustParseFloat(s, 64)
- val.SetFloat(f)
- return
- case reflect.Bool:
- b := mustParseBool(s)
- val.SetBool(b)
- return
- case reflect.Struct:
- if val.Type() == timeType {
- t, err := time.Parse(textPlistTimeLayout, s)
- if err != nil {
- panic(err)
- }
- val.Set(reflect.ValueOf(t.In(time.UTC)))
- return
- }
- fallthrough
- default:
- panic(&incompatibleDecodeTypeError{val.Type(), "string"})
- }
- }
- func (p *Decoder) unmarshal(pval cfValue, val reflect.Value) {
- if pval == nil {
- return
- }
- if val.Kind() == reflect.Ptr {
- if val.IsNil() {
- val.Set(reflect.New(val.Type().Elem()))
- }
- val = val.Elem()
- }
- if isEmptyInterface(val) {
- v := p.valueInterface(pval)
- val.Set(reflect.ValueOf(v))
- return
- }
- incompatibleTypeError := &incompatibleDecodeTypeError{val.Type(), pval.typeName()}
- // time.Time implements TextMarshaler, but we need to parse it as RFC3339
- if date, ok := pval.(cfDate); ok {
- if val.Type() == timeType {
- p.unmarshalTime(date, val)
- return
- }
- panic(incompatibleTypeError)
- }
- if receiver, can := implementsInterface(val, plistUnmarshalerType); can {
- p.unmarshalPlistInterface(pval, receiver.(Unmarshaler))
- return
- }
- if val.Type() != timeType {
- if receiver, can := implementsInterface(val, textUnmarshalerType); can {
- if str, ok := pval.(cfString); ok {
- p.unmarshalTextInterface(str, receiver.(encoding.TextUnmarshaler))
- } else {
- panic(incompatibleTypeError)
- }
- return
- }
- }
- typ := val.Type()
- switch pval := pval.(type) {
- case cfString:
- if val.Kind() == reflect.String {
- val.SetString(string(pval))
- return
- }
- if p.lax {
- p.unmarshalLaxString(string(pval), val)
- return
- }
- panic(incompatibleTypeError)
- case *cfNumber:
- switch val.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- val.SetInt(int64(pval.value))
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- val.SetUint(pval.value)
- default:
- panic(incompatibleTypeError)
- }
- case *cfReal:
- if val.Kind() == reflect.Float32 || val.Kind() == reflect.Float64 {
- // TODO: Consider warning on a downcast (storing a 64-bit value in a 32-bit reflect)
- val.SetFloat(pval.value)
- } else {
- panic(incompatibleTypeError)
- }
- case cfBoolean:
- if val.Kind() == reflect.Bool {
- val.SetBool(bool(pval))
- } else {
- panic(incompatibleTypeError)
- }
- case cfData:
- if val.Kind() == reflect.Slice && typ.Elem().Kind() == reflect.Uint8 {
- val.SetBytes([]byte(pval))
- } else {
- panic(incompatibleTypeError)
- }
- case cfUID:
- if val.Type() == uidType {
- val.SetUint(uint64(pval))
- } else {
- switch val.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- val.SetInt(int64(pval))
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- val.SetUint(uint64(pval))
- default:
- panic(incompatibleTypeError)
- }
- }
- case *cfArray:
- p.unmarshalArray(pval, val)
- case *cfDictionary:
- p.unmarshalDictionary(pval, val)
- }
- }
- func (p *Decoder) unmarshalArray(a *cfArray, val reflect.Value) {
- var n int
- if val.Kind() == reflect.Slice {
- // Slice of element values.
- // Grow slice.
- cnt := len(a.values) + val.Len()
- if cnt >= val.Cap() {
- ncap := 2 * cnt
- if ncap < 4 {
- ncap = 4
- }
- new := reflect.MakeSlice(val.Type(), val.Len(), ncap)
- reflect.Copy(new, val)
- val.Set(new)
- }
- n = val.Len()
- val.SetLen(cnt)
- } else if val.Kind() == reflect.Array {
- if len(a.values) > val.Cap() {
- panic(fmt.Errorf("plist: attempted to unmarshal %d values into an array of size %d", len(a.values), val.Cap()))
- }
- } else {
- panic(&incompatibleDecodeTypeError{val.Type(), a.typeName()})
- }
- // Recur to read element into slice.
- for _, sval := range a.values {
- p.unmarshal(sval, val.Index(n))
- n++
- }
- }
- func (p *Decoder) unmarshalDictionary(dict *cfDictionary, val reflect.Value) {
- typ := val.Type()
- switch val.Kind() {
- case reflect.Struct:
- tinfo, err := getTypeInfo(typ)
- if err != nil {
- panic(err)
- }
- entries := make(map[string]cfValue, len(dict.keys))
- for i, k := range dict.keys {
- sval := dict.values[i]
- entries[k] = sval
- }
- for _, finfo := range tinfo.fields {
- p.unmarshal(entries[finfo.name], finfo.value(val))
- }
- case reflect.Map:
- if val.IsNil() {
- val.Set(reflect.MakeMap(typ))
- }
- for i, k := range dict.keys {
- sval := dict.values[i]
- keyv := reflect.ValueOf(k).Convert(typ.Key())
- mapElem := reflect.New(typ.Elem()).Elem()
- p.unmarshal(sval, mapElem)
- val.SetMapIndex(keyv, mapElem)
- }
- default:
- panic(&incompatibleDecodeTypeError{typ, dict.typeName()})
- }
- }
- /* *Interface is modelled after encoding/json */
- func (p *Decoder) valueInterface(pval cfValue) interface{} {
- switch pval := pval.(type) {
- case cfString:
- return string(pval)
- case *cfNumber:
- if pval.signed {
- return int64(pval.value)
- }
- return pval.value
- case *cfReal:
- if pval.wide {
- return pval.value
- }
- return float32(pval.value)
- case cfBoolean:
- return bool(pval)
- case *cfArray:
- return p.arrayInterface(pval)
- case *cfDictionary:
- return p.dictionaryInterface(pval)
- case cfData:
- return []byte(pval)
- case cfDate:
- return time.Time(pval)
- case cfUID:
- return UID(pval)
- }
- return nil
- }
- func (p *Decoder) arrayInterface(a *cfArray) []interface{} {
- out := make([]interface{}, len(a.values))
- for i, subv := range a.values {
- out[i] = p.valueInterface(subv)
- }
- return out
- }
- func (p *Decoder) dictionaryInterface(dict *cfDictionary) map[string]interface{} {
- out := make(map[string]interface{})
- for i, k := range dict.keys {
- subv := dict.values[i]
- out[k] = p.valueInterface(subv)
- }
- return out
- }
|