github.com/timstclair/heapster@v0.20.0-alpha1/Godeps/_workspace/src/google.golang.org/appengine/datastore/prop.go (about)

     1  // Copyright 2011 Google Inc. All rights reserved.
     2  // Use of this source code is governed by the Apache 2.0
     3  // license that can be found in the LICENSE file.
     4  
     5  package datastore
     6  
     7  import (
     8  	"fmt"
     9  	"reflect"
    10  	"strings"
    11  	"sync"
    12  	"unicode"
    13  )
    14  
    15  // Entities with more than this many indexed properties will not be saved.
    16  const maxIndexedProperties = 20000
    17  
    18  // []byte fields more than 1 megabyte long will not be loaded or saved.
    19  const maxBlobLen = 1 << 20
    20  
    21  // Property is a name/value pair plus some metadata. A datastore entity's
    22  // contents are loaded and saved as a sequence of Properties. An entity can
    23  // have multiple Properties with the same name, provided that p.Multiple is
    24  // true on all of that entity's Properties with that name.
    25  type Property struct {
    26  	// Name is the property name.
    27  	Name string
    28  	// Value is the property value. The valid types are:
    29  	//	- int64
    30  	//	- bool
    31  	//	- string
    32  	//	- float64
    33  	//	- ByteString
    34  	//	- *Key
    35  	//	- time.Time
    36  	//	- appengine.BlobKey
    37  	//	- appengine.GeoPoint
    38  	//	- []byte (up to 1 megabyte in length)
    39  	// This set is smaller than the set of valid struct field types that the
    40  	// datastore can load and save. A Property Value cannot be a slice (apart
    41  	// from []byte); use multiple Properties instead. Also, a Value's type
    42  	// must be explicitly on the list above; it is not sufficient for the
    43  	// underlying type to be on that list. For example, a Value of "type
    44  	// myInt64 int64" is invalid. Smaller-width integers and floats are also
    45  	// invalid. Again, this is more restrictive than the set of valid struct
    46  	// field types.
    47  	//
    48  	// A Value will have an opaque type when loading entities from an index,
    49  	// such as via a projection query. Load entities into a struct instead
    50  	// of a PropertyLoadSaver when using a projection query.
    51  	//
    52  	// A Value may also be the nil interface value; this is equivalent to
    53  	// Python's None but not directly representable by a Go struct. Loading
    54  	// a nil-valued property into a struct will set that field to the zero
    55  	// value.
    56  	Value interface{}
    57  	// NoIndex is whether the datastore cannot index this property.
    58  	NoIndex bool
    59  	// Multiple is whether the entity can have multiple properties with
    60  	// the same name. Even if a particular instance only has one property with
    61  	// a certain name, Multiple should be true if a struct would best represent
    62  	// it as a field of type []T instead of type T.
    63  	Multiple bool
    64  }
    65  
    66  // ByteString is a short byte slice (up to 1500 bytes) that can be indexed.
    67  type ByteString []byte
    68  
    69  // PropertyLoadSaver can be converted from and to a slice of Properties.
    70  type PropertyLoadSaver interface {
    71  	Load([]Property) error
    72  	Save() ([]Property, error)
    73  }
    74  
    75  // PropertyList converts a []Property to implement PropertyLoadSaver.
    76  type PropertyList []Property
    77  
    78  var (
    79  	typeOfPropertyLoadSaver = reflect.TypeOf((*PropertyLoadSaver)(nil)).Elem()
    80  	typeOfPropertyList      = reflect.TypeOf(PropertyList(nil))
    81  )
    82  
    83  // Load loads all of the provided properties into l.
    84  // It does not first reset *l to an empty slice.
    85  func (l *PropertyList) Load(p []Property) error {
    86  	*l = append(*l, p...)
    87  	return nil
    88  }
    89  
    90  // Save saves all of l's properties as a slice or Properties.
    91  func (l *PropertyList) Save() ([]Property, error) {
    92  	return *l, nil
    93  }
    94  
    95  // validPropertyName returns whether name consists of one or more valid Go
    96  // identifiers joined by ".".
    97  func validPropertyName(name string) bool {
    98  	if name == "" {
    99  		return false
   100  	}
   101  	for _, s := range strings.Split(name, ".") {
   102  		if s == "" {
   103  			return false
   104  		}
   105  		first := true
   106  		for _, c := range s {
   107  			if first {
   108  				first = false
   109  				if c != '_' && !unicode.IsLetter(c) {
   110  					return false
   111  				}
   112  			} else {
   113  				if c != '_' && !unicode.IsLetter(c) && !unicode.IsDigit(c) {
   114  					return false
   115  				}
   116  			}
   117  		}
   118  	}
   119  	return true
   120  }
   121  
   122  // structTag is the parsed `datastore:"name,options"` tag of a struct field.
   123  // If a field has no tag, or the tag has an empty name, then the structTag's
   124  // name is just the field name. A "-" name means that the datastore ignores
   125  // that field.
   126  type structTag struct {
   127  	name    string
   128  	noIndex bool
   129  }
   130  
   131  // structCodec describes how to convert a struct to and from a sequence of
   132  // properties.
   133  type structCodec struct {
   134  	// byIndex gives the structTag for the i'th field.
   135  	byIndex []structTag
   136  	// byName gives the field codec for the structTag with the given name.
   137  	byName map[string]fieldCodec
   138  	// hasSlice is whether a struct or any of its nested or embedded structs
   139  	// has a slice-typed field (other than []byte).
   140  	hasSlice bool
   141  	// complete is whether the structCodec is complete. An incomplete
   142  	// structCodec may be encountered when walking a recursive struct.
   143  	complete bool
   144  }
   145  
   146  // fieldCodec is a struct field's index and, if that struct field's type is
   147  // itself a struct, that substruct's structCodec.
   148  type fieldCodec struct {
   149  	index          int
   150  	substructCodec *structCodec
   151  }
   152  
   153  // structCodecs collects the structCodecs that have already been calculated.
   154  var (
   155  	structCodecsMutex sync.Mutex
   156  	structCodecs      = make(map[reflect.Type]*structCodec)
   157  )
   158  
   159  // getStructCodec returns the structCodec for the given struct type.
   160  func getStructCodec(t reflect.Type) (*structCodec, error) {
   161  	structCodecsMutex.Lock()
   162  	defer structCodecsMutex.Unlock()
   163  	return getStructCodecLocked(t)
   164  }
   165  
   166  // getStructCodecLocked implements getStructCodec. The structCodecsMutex must
   167  // be held when calling this function.
   168  func getStructCodecLocked(t reflect.Type) (ret *structCodec, retErr error) {
   169  	c, ok := structCodecs[t]
   170  	if ok {
   171  		return c, nil
   172  	}
   173  	c = &structCodec{
   174  		byIndex: make([]structTag, t.NumField()),
   175  		byName:  make(map[string]fieldCodec),
   176  	}
   177  
   178  	// Add c to the structCodecs map before we are sure it is good. If t is
   179  	// a recursive type, it needs to find the incomplete entry for itself in
   180  	// the map.
   181  	structCodecs[t] = c
   182  	defer func() {
   183  		if retErr != nil {
   184  			delete(structCodecs, t)
   185  		}
   186  	}()
   187  
   188  	for i := range c.byIndex {
   189  		f := t.Field(i)
   190  		name, opts := f.Tag.Get("datastore"), ""
   191  		if i := strings.Index(name, ","); i != -1 {
   192  			name, opts = name[:i], name[i+1:]
   193  		}
   194  		if name == "" {
   195  			if !f.Anonymous {
   196  				name = f.Name
   197  			}
   198  		} else if name == "-" {
   199  			c.byIndex[i] = structTag{name: name}
   200  			continue
   201  		} else if !validPropertyName(name) {
   202  			return nil, fmt.Errorf("datastore: struct tag has invalid property name: %q", name)
   203  		}
   204  
   205  		substructType, fIsSlice := reflect.Type(nil), false
   206  		switch f.Type.Kind() {
   207  		case reflect.Struct:
   208  			substructType = f.Type
   209  		case reflect.Slice:
   210  			if f.Type.Elem().Kind() == reflect.Struct {
   211  				substructType = f.Type.Elem()
   212  			}
   213  			fIsSlice = f.Type != typeOfByteSlice
   214  			c.hasSlice = c.hasSlice || fIsSlice
   215  		}
   216  
   217  		if substructType != nil && substructType != typeOfTime && substructType != typeOfGeoPoint {
   218  			if name != "" {
   219  				name = name + "."
   220  			}
   221  			sub, err := getStructCodecLocked(substructType)
   222  			if err != nil {
   223  				return nil, err
   224  			}
   225  			if !sub.complete {
   226  				return nil, fmt.Errorf("datastore: recursive struct: field %q", f.Name)
   227  			}
   228  			if fIsSlice && sub.hasSlice {
   229  				return nil, fmt.Errorf(
   230  					"datastore: flattening nested structs leads to a slice of slices: field %q", f.Name)
   231  			}
   232  			c.hasSlice = c.hasSlice || sub.hasSlice
   233  			for relName := range sub.byName {
   234  				absName := name + relName
   235  				if _, ok := c.byName[absName]; ok {
   236  					return nil, fmt.Errorf("datastore: struct tag has repeated property name: %q", absName)
   237  				}
   238  				c.byName[absName] = fieldCodec{index: i, substructCodec: sub}
   239  			}
   240  		} else {
   241  			if _, ok := c.byName[name]; ok {
   242  				return nil, fmt.Errorf("datastore: struct tag has repeated property name: %q", name)
   243  			}
   244  			c.byName[name] = fieldCodec{index: i}
   245  		}
   246  
   247  		c.byIndex[i] = structTag{
   248  			name:    name,
   249  			noIndex: opts == "noindex",
   250  		}
   251  	}
   252  	c.complete = true
   253  	return c, nil
   254  }
   255  
   256  // structPLS adapts a struct to be a PropertyLoadSaver.
   257  type structPLS struct {
   258  	v     reflect.Value
   259  	codec *structCodec
   260  }
   261  
   262  // newStructPLS returns a PropertyLoadSaver for the struct pointer p.
   263  func newStructPLS(p interface{}) (PropertyLoadSaver, error) {
   264  	v := reflect.ValueOf(p)
   265  	if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
   266  		return nil, ErrInvalidEntityType
   267  	}
   268  	v = v.Elem()
   269  	codec, err := getStructCodec(v.Type())
   270  	if err != nil {
   271  		return nil, err
   272  	}
   273  	return structPLS{v, codec}, nil
   274  }
   275  
   276  // LoadStruct loads the properties from p to dst.
   277  // dst must be a struct pointer.
   278  func LoadStruct(dst interface{}, p []Property) error {
   279  	x, err := newStructPLS(dst)
   280  	if err != nil {
   281  		return err
   282  	}
   283  	return x.Load(p)
   284  }
   285  
   286  // SaveStruct returns the properties from src as a slice of Properties.
   287  // src must be a struct pointer.
   288  func SaveStruct(src interface{}) ([]Property, error) {
   289  	x, err := newStructPLS(src)
   290  	if err != nil {
   291  		return nil, err
   292  	}
   293  	return x.Save()
   294  }