| // Copyright 2011 Google Inc. All rights reserved. | |
| // Use of this source code is governed by the Apache 2.0 | |
| // license that can be found in the LICENSE file. | |
| package datastore | |
| import ( | |
| "fmt" | |
| "reflect" | |
| "strings" | |
| "time" | |
| "github.com/golang/protobuf/proto" | |
| "google.golang.org/appengine" | |
| pb "google.golang.org/appengine/internal/datastore" | |
| ) | |
| var ( | |
| typeOfBlobKey = reflect.TypeOf(appengine.BlobKey("")) | |
| typeOfByteSlice = reflect.TypeOf([]byte(nil)) | |
| 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(pValue interface{}, v reflect.Value) string { | |
| entityType := "empty" | |
| switch pValue.(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 appengine.BlobKey: | |
| entityType = "appengine.BlobKey" | |
| case appengine.GeoPoint: | |
| entityType = "appengine.GeoPoint" | |
| case ByteString: | |
| entityType = "datastore.ByteString" | |
| 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, requireSlice bool) string { | |
| var v reflect.Value | |
| 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 = initField(structValue, decoder.path) | |
| if !v.IsValid() { | |
| return "no such struct field" | |
| } | |
| if !v.CanSet() { | |
| return "cannot set struct field" | |
| } | |
| if decoder.structCodec != nil { | |
| codec = decoder.structCodec | |
| structValue = v | |
| } | |
| if v.Kind() == reflect.Slice && v.Type() != typeOfByteSlice { | |
| if l.m == nil { | |
| l.m = make(map[string]int) | |
| } | |
| 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(sliceIndex) | |
| requireSlice = false | |
| } | |
| } | |
| 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 requireSlice { | |
| return "multiple-valued property requires a slice field type" | |
| } | |
| // Convert indexValues to a Go value with a meaning derived from the | |
| // destination type. | |
| pValue := p.Value | |
| if iv, ok := pValue.(indexValue); ok { | |
| meaning := pb.Property_NO_MEANING | |
| switch v.Type() { | |
| case typeOfBlobKey: | |
| meaning = pb.Property_BLOBKEY | |
| case typeOfByteSlice: | |
| meaning = pb.Property_BLOB | |
| case typeOfByteString: | |
| meaning = pb.Property_BYTESTRING | |
| case typeOfGeoPoint: | |
| 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) | |
| if err != nil { | |
| return err.Error() | |
| } | |
| } | |
| 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(pValue, 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(pValue, v) | |
| } | |
| v.SetBool(x) | |
| case reflect.String: | |
| switch x := pValue.(type) { | |
| case appengine.BlobKey: | |
| v.SetString(string(x)) | |
| case ByteString: | |
| v.SetString(string(x)) | |
| case string: | |
| v.SetString(x) | |
| default: | |
| if pValue != nil { | |
| return typeMismatchReason(pValue, v) | |
| } | |
| } | |
| case reflect.Float32, reflect.Float64: | |
| x, ok := pValue.(float64) | |
| if !ok && pValue != nil { | |
| return typeMismatchReason(pValue, 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(pValue, v) | |
| } | |
| if _, ok := v.Interface().(*Key); !ok { | |
| return typeMismatchReason(pValue, 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(pValue, v) | |
| } | |
| v.Set(reflect.ValueOf(x)) | |
| case typeOfGeoPoint: | |
| x, ok := pValue.(appengine.GeoPoint) | |
| if !ok && pValue != nil { | |
| return typeMismatchReason(pValue, v) | |
| } | |
| v.Set(reflect.ValueOf(x)) | |
| default: | |
| 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) | |
| if !ok { | |
| if y, yok := pValue.(ByteString); yok { | |
| x, ok = []byte(y), true | |
| } | |
| } | |
| if !ok && pValue != nil { | |
| return typeMismatchReason(pValue, v) | |
| } | |
| if v.Type().Elem().Kind() != reflect.Uint8 { | |
| return typeMismatchReason(pValue, v) | |
| } | |
| v.SetBytes(x) | |
| default: | |
| 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) { | |
| ent, err := protoToEntity(src) | |
| if err != nil { | |
| return err | |
| } | |
| if e, ok := dst.(PropertyLoadSaver); ok { | |
| return e.Load(ent.Properties) | |
| } | |
| return LoadStruct(dst, ent.Properties) | |
| } | |
| func (s structPLS) Load(props []Property) error { | |
| var fieldName, reason string | |
| var l propertyLoader | |
| for _, p := range props { | |
| if errStr := l.load(s.codec, s.v, p, p.Multiple); 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 protoToEntity(src *pb.EntityProto) (*Entity, error) { | |
| props, rawProps := src.Property, src.RawProperty | |
| outProps := make([]Property, 0, len(props)+len(rawProps)) | |
| for { | |
| var ( | |
| x *pb.Property | |
| noIndex bool | |
| ) | |
| if len(props) > 0 { | |
| x, props = props[0], props[1:] | |
| } else if len(rawProps) > 0 { | |
| x, rawProps = rawProps[0], rawProps[1:] | |
| noIndex = true | |
| } else { | |
| break | |
| } | |
| var value interface{} | |
| if x.Meaning != nil && *x.Meaning == pb.Property_INDEX_VALUE { | |
| value = indexValue{x.Value} | |
| } else { | |
| var err error | |
| value, err = propValue(x.Value, x.GetMeaning()) | |
| if err != nil { | |
| return nil, err | |
| } | |
| } | |
| outProps = append(outProps, Property{ | |
| Name: x.GetName(), | |
| Value: value, | |
| NoIndex: noIndex, | |
| Multiple: x.GetMultiple(), | |
| }) | |
| } | |
| 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 | |
| // meaning. For example, an Int64Value with GD_WHEN becomes a time.Time. | |
| func propValue(v *pb.PropertyValue, m pb.Property_Meaning) (interface{}, error) { | |
| switch { | |
| case v.Int64Value != nil: | |
| if m == pb.Property_GD_WHEN { | |
| return fromUnixMicro(*v.Int64Value), nil | |
| } else { | |
| return *v.Int64Value, nil | |
| } | |
| case v.BooleanValue != nil: | |
| return *v.BooleanValue, nil | |
| case v.StringValue != nil: | |
| if m == pb.Property_BLOB { | |
| return []byte(*v.StringValue), nil | |
| } else if m == pb.Property_BLOBKEY { | |
| 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 | |
| } | |
| case v.DoubleValue != nil: | |
| return *v.DoubleValue, nil | |
| case v.Referencevalue != nil: | |
| key, err := referenceValueToKey(v.Referencevalue) | |
| if err != nil { | |
| return nil, err | |
| } | |
| return key, nil | |
| case v.Pointvalue != nil: | |
| // NOTE: Strangely, latitude maps to X, longitude to Y. | |
| return appengine.GeoPoint{Lat: v.Pointvalue.GetX(), Lng: v.Pointvalue.GetY()}, nil | |
| } | |
| return nil, nil | |
| } | |
| // indexValue is a Property value that is created when entities are loaded from | |
| // an index, such as from a projection query. | |
| // | |
| // Such Property values do not contain all of the metadata required to be | |
| // faithfully represented as a Go value, and are instead represented as an | |
| // opaque indexValue. Load the properties into a concrete struct type (e.g. by | |
| // passing a struct pointer to Iterator.Next) to reconstruct actual Go values | |
| // of type int, string, time.Time, etc. | |
| type indexValue struct { | |
| value *pb.PropertyValue | |
| } |