123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277 |
- // Copyright 2014 Google Inc. All Rights Reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package datastore
- import (
- "fmt"
- "reflect"
- "time"
- pb "google.golang.org/cloud/internal/datastore"
- )
- var (
- typeOfByteSlice = reflect.TypeOf([]byte(nil))
- typeOfTime = reflect.TypeOf(time.Time{})
- )
- // typeMismatchReason returns a string explaining why the property p could not
- // be stored in an entity field of type v.Type().
- func typeMismatchReason(p Property, v reflect.Value) string {
- entityType := "empty"
- switch p.Value.(type) {
- case int64:
- entityType = "int"
- case bool:
- entityType = "bool"
- case string:
- entityType = "string"
- case float64:
- entityType = "float"
- case *Key:
- entityType = "*datastore.Key"
- case time.Time:
- entityType = "time.Time"
- case []byte:
- entityType = "[]byte"
- }
- return fmt.Sprintf("type mismatch: %s versus %v", entityType, v.Type())
- }
- type propertyLoader struct {
- // m holds the number of times a substruct field like "Foo.Bar.Baz" has
- // been seen so far. The map is constructed lazily.
- m map[string]int
- }
- func (l *propertyLoader) load(codec *structCodec, structValue reflect.Value, p Property, prev map[string]struct{}) string {
- var sliceOk bool
- var v reflect.Value
- // Traverse a struct's struct-typed fields.
- for name := p.Name; ; {
- decoder, ok := codec.byName[name]
- if !ok {
- return "no such struct field"
- }
- v = structValue.Field(decoder.index)
- if !v.IsValid() {
- return "no such struct field"
- }
- if !v.CanSet() {
- return "cannot set struct field"
- }
- if decoder.substructCodec == nil {
- break
- }
- if v.Kind() == reflect.Slice {
- if l.m == nil {
- l.m = make(map[string]int)
- }
- index := l.m[p.Name]
- l.m[p.Name] = index + 1
- for v.Len() <= index {
- v.Set(reflect.Append(v, reflect.New(v.Type().Elem()).Elem()))
- }
- structValue = v.Index(index)
- sliceOk = true
- } else {
- structValue = v
- }
- // Strip the "I." from "I.X".
- name = name[len(codec.byIndex[decoder.index].name):]
- codec = decoder.substructCodec
- }
- var slice reflect.Value
- if v.Kind() == reflect.Slice && v.Type().Elem().Kind() != reflect.Uint8 {
- slice = v
- v = reflect.New(v.Type().Elem()).Elem()
- } else if _, ok := prev[p.Name]; ok && !sliceOk {
- // Zero the field back out that was set previously, turns out its a slice and we don't know what to do with it
- v.Set(reflect.Zero(v.Type()))
- return "multiple-valued property requires a slice field type"
- }
- prev[p.Name] = struct{}{}
- pValue := p.Value
- switch v.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- x, ok := pValue.(int64)
- if !ok && pValue != nil {
- return typeMismatchReason(p, v)
- }
- if v.OverflowInt(x) {
- return fmt.Sprintf("value %v overflows struct field of type %v", x, v.Type())
- }
- v.SetInt(x)
- case reflect.Bool:
- x, ok := pValue.(bool)
- if !ok && pValue != nil {
- return typeMismatchReason(p, v)
- }
- v.SetBool(x)
- case reflect.String:
- x, ok := pValue.(string)
- if !ok && pValue != nil {
- return typeMismatchReason(p, v)
- }
- v.SetString(x)
- case reflect.Float32, reflect.Float64:
- x, ok := pValue.(float64)
- if !ok && pValue != nil {
- return typeMismatchReason(p, v)
- }
- if v.OverflowFloat(x) {
- return fmt.Sprintf("value %v overflows struct field of type %v", x, v.Type())
- }
- v.SetFloat(x)
- case reflect.Ptr:
- x, ok := pValue.(*Key)
- if !ok && pValue != nil {
- return typeMismatchReason(p, v)
- }
- if _, ok := v.Interface().(*Key); !ok {
- return typeMismatchReason(p, v)
- }
- v.Set(reflect.ValueOf(x))
- case reflect.Struct:
- switch v.Type() {
- case typeOfTime:
- x, ok := pValue.(time.Time)
- if !ok && pValue != nil {
- return typeMismatchReason(p, v)
- }
- v.Set(reflect.ValueOf(x))
- default:
- return typeMismatchReason(p, v)
- }
- case reflect.Slice:
- x, ok := pValue.([]byte)
- if !ok && pValue != nil {
- return typeMismatchReason(p, v)
- }
- if v.Type().Elem().Kind() != reflect.Uint8 {
- return typeMismatchReason(p, v)
- }
- v.SetBytes(x)
- default:
- return typeMismatchReason(p, v)
- }
- if slice.IsValid() {
- slice.Set(reflect.Append(slice, v))
- }
- return ""
- }
- // loadEntity loads an EntityProto into PropertyLoadSaver or struct pointer.
- func loadEntity(dst interface{}, src *pb.Entity) (err error) {
- props := protoToProperties(src)
- if e, ok := dst.(PropertyLoadSaver); ok {
- return e.Load(props)
- }
- return LoadStruct(dst, props)
- }
- func (s structPLS) Load(props []Property) error {
- var fieldName, reason string
- var l propertyLoader
- prev := make(map[string]struct{})
- for _, p := range props {
- if errStr := l.load(s.codec, s.v, p, prev); errStr != "" {
- // We don't return early, as we try to load as many properties as possible.
- // It is valid to load an entity into a struct that cannot fully represent it.
- // That case returns an error, but the caller is free to ignore it.
- fieldName, reason = p.Name, errStr
- }
- }
- if reason != "" {
- return &ErrFieldMismatch{
- StructType: s.v.Type(),
- FieldName: fieldName,
- Reason: reason,
- }
- }
- return nil
- }
- func protoToProperties(src *pb.Entity) []Property {
- props := src.Property
- out := make([]Property, 0, len(props))
- for {
- var (
- x *pb.Property
- noIndex bool
- )
- if len(props) > 0 {
- x, props = props[0], props[1:]
- noIndex = !x.GetValue().GetIndexed()
- } else {
- break
- }
- if x.Value.ListValue == nil {
- out = append(out, Property{
- Name: x.GetName(),
- Value: propValue(x.Value),
- NoIndex: noIndex,
- Multiple: false,
- })
- } else {
- for _, v := range x.Value.ListValue {
- out = append(out, Property{
- Name: x.GetName(),
- Value: propValue(v),
- NoIndex: noIndex,
- Multiple: true,
- })
- }
- }
- }
- return out
- }
- // propValue returns a Go value that combines the raw PropertyValue with a
- // meaning. For example, an Int64Value with GD_WHEN becomes a time.Time.
- func propValue(v *pb.Value) interface{} {
- //TODO(PSG-Luna): Support EntityValue
- //TODO(PSG-Luna): GeoPoint seems gone from the v1 proto, reimplement it once it's readded
- switch {
- case v.IntegerValue != nil:
- return *v.IntegerValue
- case v.TimestampMicrosecondsValue != nil:
- return fromUnixMicro(*v.TimestampMicrosecondsValue)
- case v.BooleanValue != nil:
- return *v.BooleanValue
- case v.StringValue != nil:
- return *v.StringValue
- case v.BlobValue != nil:
- return []byte(v.BlobValue)
- case v.BlobKeyValue != nil:
- return *v.BlobKeyValue
- case v.DoubleValue != nil:
- return *v.DoubleValue
- case v.KeyValue != nil:
- // TODO(djd): Don't drop this error.
- key, _ := protoToKey(v.KeyValue)
- return key
- }
- return nil
- }
|