aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/google.golang.org/appengine/datastore/load.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/google.golang.org/appengine/datastore/load.go')
-rw-r--r--vendor/google.golang.org/appengine/datastore/load.go175
1 files changed, 135 insertions, 40 deletions
diff --git a/vendor/google.golang.org/appengine/datastore/load.go b/vendor/google.golang.org/appengine/datastore/load.go
index 3f3c80c..38a6365 100644
--- a/vendor/google.golang.org/appengine/datastore/load.go
+++ b/vendor/google.golang.org/appengine/datastore/load.go
@@ -7,8 +7,10 @@ package datastore
import (
"fmt"
"reflect"
+ "strings"
"time"
+ "github.com/golang/protobuf/proto"
"google.golang.org/appengine"
pb "google.golang.org/appengine/internal/datastore"
)
@@ -19,13 +21,15 @@ var (
typeOfByteString = reflect.TypeOf(ByteString(nil))
typeOfGeoPoint = reflect.TypeOf(appengine.GeoPoint{})
typeOfTime = reflect.TypeOf(time.Time{})
+ typeOfKeyPtr = reflect.TypeOf(&Key{})
+ typeOfEntityPtr = reflect.TypeOf(&Entity{})
)
// 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 {
+func typeMismatchReason(pValue interface{}, v reflect.Value) string {
entityType := "empty"
- switch p.Value.(type) {
+ switch pValue.(type) {
case int64:
entityType = "int"
case bool:
@@ -58,13 +62,41 @@ type propertyLoader struct {
func (l *propertyLoader) load(codec *structCodec, structValue reflect.Value, p Property, requireSlice bool) string {
var v reflect.Value
- // Traverse a struct's struct-typed fields.
- for name := p.Name; ; {
- decoder, ok := codec.byName[name]
+ var sliceIndex int
+
+ name := p.Name
+
+ // If name ends with a '.', the last field is anonymous.
+ // In this case, strings.Split will give us "" as the
+ // last element of our fields slice, which will match the ""
+ // field name in the substruct codec.
+ fields := strings.Split(name, ".")
+
+ for len(fields) > 0 {
+ var decoder fieldCodec
+ var ok bool
+
+ // Cut off the last field (delimited by ".") and find its parent
+ // in the codec.
+ // eg. for name "A.B.C.D", split off "A.B.C" and try to
+ // find a field in the codec with this name.
+ // Loop again with "A.B", etc.
+ for i := len(fields); i > 0; i-- {
+ parent := strings.Join(fields[:i], ".")
+ decoder, ok = codec.fields[parent]
+ if ok {
+ fields = fields[i:]
+ break
+ }
+ }
+
+ // If we never found a matching field in the codec, return
+ // error message.
if !ok {
return "no such struct field"
}
- v = structValue.Field(decoder.index)
+
+ v = initField(structValue, decoder.path)
if !v.IsValid() {
return "no such struct field"
}
@@ -72,27 +104,23 @@ func (l *propertyLoader) load(codec *structCodec, structValue reflect.Value, p P
return "cannot set struct field"
}
- if decoder.substructCodec == nil {
- break
+ if decoder.structCodec != nil {
+ codec = decoder.structCodec
+ structValue = v
}
- if v.Kind() == reflect.Slice {
+ if v.Kind() == reflect.Slice && v.Type() != typeOfByteSlice {
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 {
+ sliceIndex = l.m[p.Name]
+ l.m[p.Name] = sliceIndex + 1
+ for v.Len() <= sliceIndex {
v.Set(reflect.Append(v, reflect.New(v.Type().Elem()).Elem()))
}
- structValue = v.Index(index)
+ structValue = v.Index(sliceIndex)
requireSlice = false
- } else {
- structValue = v
}
- // Strip the "I." from "I.X".
- name = name[len(codec.byIndex[decoder.index].name):]
- codec = decoder.substructCodec
}
var slice reflect.Value
@@ -119,6 +147,8 @@ func (l *propertyLoader) load(codec *structCodec, structValue reflect.Value, p P
meaning = pb.Property_GEORSS_POINT
case typeOfTime:
meaning = pb.Property_GD_WHEN
+ case typeOfEntityPtr:
+ meaning = pb.Property_ENTITY_PROTO
}
var err error
pValue, err = propValue(iv.value, meaning)
@@ -127,11 +157,28 @@ func (l *propertyLoader) load(codec *structCodec, structValue reflect.Value, p P
}
}
+ if errReason := setVal(v, pValue); errReason != "" {
+ // Set the slice back to its zero value.
+ if slice.IsValid() {
+ slice.Set(reflect.Zero(slice.Type()))
+ }
+ return errReason
+ }
+
+ if slice.IsValid() {
+ slice.Index(sliceIndex).Set(v)
+ }
+
+ return ""
+}
+
+// setVal sets v to the value pValue.
+func setVal(v reflect.Value, pValue interface{}) string {
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)
+ return typeMismatchReason(pValue, v)
}
if v.OverflowInt(x) {
return fmt.Sprintf("value %v overflows struct field of type %v", x, v.Type())
@@ -140,7 +187,7 @@ func (l *propertyLoader) load(codec *structCodec, structValue reflect.Value, p P
case reflect.Bool:
x, ok := pValue.(bool)
if !ok && pValue != nil {
- return typeMismatchReason(p, v)
+ return typeMismatchReason(pValue, v)
}
v.SetBool(x)
case reflect.String:
@@ -153,13 +200,13 @@ func (l *propertyLoader) load(codec *structCodec, structValue reflect.Value, p P
v.SetString(x)
default:
if pValue != nil {
- return typeMismatchReason(p, v)
+ return typeMismatchReason(pValue, v)
}
}
case reflect.Float32, reflect.Float64:
x, ok := pValue.(float64)
if !ok && pValue != nil {
- return typeMismatchReason(p, v)
+ return typeMismatchReason(pValue, v)
}
if v.OverflowFloat(x) {
return fmt.Sprintf("value %v overflows struct field of type %v", x, v.Type())
@@ -168,10 +215,10 @@ func (l *propertyLoader) load(codec *structCodec, structValue reflect.Value, p P
case reflect.Ptr:
x, ok := pValue.(*Key)
if !ok && pValue != nil {
- return typeMismatchReason(p, v)
+ return typeMismatchReason(pValue, v)
}
if _, ok := v.Interface().(*Key); !ok {
- return typeMismatchReason(p, v)
+ return typeMismatchReason(pValue, v)
}
v.Set(reflect.ValueOf(x))
case reflect.Struct:
@@ -179,17 +226,38 @@ func (l *propertyLoader) load(codec *structCodec, structValue reflect.Value, p P
case typeOfTime:
x, ok := pValue.(time.Time)
if !ok && pValue != nil {
- return typeMismatchReason(p, v)
+ return typeMismatchReason(pValue, v)
}
v.Set(reflect.ValueOf(x))
case typeOfGeoPoint:
x, ok := pValue.(appengine.GeoPoint)
if !ok && pValue != nil {
- return typeMismatchReason(p, v)
+ return typeMismatchReason(pValue, v)
}
v.Set(reflect.ValueOf(x))
default:
- return typeMismatchReason(p, v)
+ ent, ok := pValue.(*Entity)
+ if !ok {
+ return typeMismatchReason(pValue, v)
+ }
+
+ // Recursively load nested struct
+ pls, err := newStructPLS(v.Addr().Interface())
+ if err != nil {
+ return err.Error()
+ }
+
+ // if ent has a Key value and our struct has a Key field,
+ // load the Entity's Key value into the Key field on the struct.
+ if ent.Key != nil && pls.codec.keyField != -1 {
+
+ pls.v.Field(pls.codec.keyField).Set(reflect.ValueOf(ent.Key))
+ }
+
+ err = pls.Load(ent.Properties)
+ if err != nil {
+ return err.Error()
+ }
}
case reflect.Slice:
x, ok := pValue.([]byte)
@@ -199,31 +267,44 @@ func (l *propertyLoader) load(codec *structCodec, structValue reflect.Value, p P
}
}
if !ok && pValue != nil {
- return typeMismatchReason(p, v)
+ return typeMismatchReason(pValue, v)
}
if v.Type().Elem().Kind() != reflect.Uint8 {
- return typeMismatchReason(p, v)
+ return typeMismatchReason(pValue, v)
}
v.SetBytes(x)
default:
- return typeMismatchReason(p, v)
- }
- if slice.IsValid() {
- slice.Set(reflect.Append(slice, v))
+ return typeMismatchReason(pValue, v)
}
return ""
}
+// initField is similar to reflect's Value.FieldByIndex, in that it
+// returns the nested struct field corresponding to index, but it
+// initialises any nil pointers encountered when traversing the structure.
+func initField(val reflect.Value, index []int) reflect.Value {
+ for _, i := range index[:len(index)-1] {
+ val = val.Field(i)
+ if val.Kind() == reflect.Ptr {
+ if val.IsNil() {
+ val.Set(reflect.New(val.Type().Elem()))
+ }
+ val = val.Elem()
+ }
+ }
+ return val.Field(index[len(index)-1])
+}
+
// loadEntity loads an EntityProto into PropertyLoadSaver or struct pointer.
func loadEntity(dst interface{}, src *pb.EntityProto) (err error) {
- props, err := protoToProperties(src)
+ ent, err := protoToEntity(src)
if err != nil {
return err
}
if e, ok := dst.(PropertyLoadSaver); ok {
- return e.Load(props)
+ return e.Load(ent.Properties)
}
- return LoadStruct(dst, props)
+ return LoadStruct(dst, ent.Properties)
}
func (s structPLS) Load(props []Property) error {
@@ -247,9 +328,9 @@ func (s structPLS) Load(props []Property) error {
return nil
}
-func protoToProperties(src *pb.EntityProto) ([]Property, error) {
+func protoToEntity(src *pb.EntityProto) (*Entity, error) {
props, rawProps := src.Property, src.RawProperty
- out := make([]Property, 0, len(props)+len(rawProps))
+ outProps := make([]Property, 0, len(props)+len(rawProps))
for {
var (
x *pb.Property
@@ -274,14 +355,21 @@ func protoToProperties(src *pb.EntityProto) ([]Property, error) {
return nil, err
}
}
- out = append(out, Property{
+ outProps = append(outProps, Property{
Name: x.GetName(),
Value: value,
NoIndex: noIndex,
Multiple: x.GetMultiple(),
})
}
- return out, nil
+
+ var key *Key
+ if src.Key != nil {
+ // Ignore any error, since nested entity values
+ // are allowed to have an invalid key.
+ key, _ = protoToKey(src.Key)
+ }
+ return &Entity{key, outProps}, nil
}
// propValue returns a Go value that combines the raw PropertyValue with a
@@ -303,6 +391,13 @@ func propValue(v *pb.PropertyValue, m pb.Property_Meaning) (interface{}, error)
return appengine.BlobKey(*v.StringValue), nil
} else if m == pb.Property_BYTESTRING {
return ByteString(*v.StringValue), nil
+ } else if m == pb.Property_ENTITY_PROTO {
+ var ent pb.EntityProto
+ err := proto.Unmarshal([]byte(*v.StringValue), &ent)
+ if err != nil {
+ return nil, err
+ }
+ return protoToEntity(&ent)
} else {
return *v.StringValue, nil
}