github.com/timstclair/heapster@v0.20.0-alpha1/Godeps/_workspace/src/google.golang.org/appengine/datastore/key.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 "bytes" 9 "encoding/base64" 10 "encoding/gob" 11 "errors" 12 "fmt" 13 "strconv" 14 "strings" 15 16 "github.com/golang/protobuf/proto" 17 "golang.org/x/net/context" 18 19 "google.golang.org/appengine/internal" 20 pb "google.golang.org/appengine/internal/datastore" 21 ) 22 23 // Key represents the datastore key for a stored entity, and is immutable. 24 type Key struct { 25 kind string 26 stringID string 27 intID int64 28 parent *Key 29 appID string 30 namespace string 31 } 32 33 // Kind returns the key's kind (also known as entity type). 34 func (k *Key) Kind() string { 35 return k.kind 36 } 37 38 // StringID returns the key's string ID (also known as an entity name or key 39 // name), which may be "". 40 func (k *Key) StringID() string { 41 return k.stringID 42 } 43 44 // IntID returns the key's integer ID, which may be 0. 45 func (k *Key) IntID() int64 { 46 return k.intID 47 } 48 49 // Parent returns the key's parent key, which may be nil. 50 func (k *Key) Parent() *Key { 51 return k.parent 52 } 53 54 // AppID returns the key's application ID. 55 func (k *Key) AppID() string { 56 return k.appID 57 } 58 59 // Namespace returns the key's namespace. 60 func (k *Key) Namespace() string { 61 return k.namespace 62 } 63 64 // Incomplete returns whether the key does not refer to a stored entity. 65 // In particular, whether the key has a zero StringID and a zero IntID. 66 func (k *Key) Incomplete() bool { 67 return k.stringID == "" && k.intID == 0 68 } 69 70 // valid returns whether the key is valid. 71 func (k *Key) valid() bool { 72 if k == nil { 73 return false 74 } 75 for ; k != nil; k = k.parent { 76 if k.kind == "" || k.appID == "" { 77 return false 78 } 79 if k.stringID != "" && k.intID != 0 { 80 return false 81 } 82 if k.parent != nil { 83 if k.parent.Incomplete() { 84 return false 85 } 86 if k.parent.appID != k.appID || k.parent.namespace != k.namespace { 87 return false 88 } 89 } 90 } 91 return true 92 } 93 94 // Equal returns whether two keys are equal. 95 func (k *Key) Equal(o *Key) bool { 96 for k != nil && o != nil { 97 if k.kind != o.kind || k.stringID != o.stringID || k.intID != o.intID || k.appID != o.appID || k.namespace != o.namespace { 98 return false 99 } 100 k, o = k.parent, o.parent 101 } 102 return k == o 103 } 104 105 // root returns the furthest ancestor of a key, which may be itself. 106 func (k *Key) root() *Key { 107 for k.parent != nil { 108 k = k.parent 109 } 110 return k 111 } 112 113 // marshal marshals the key's string representation to the buffer. 114 func (k *Key) marshal(b *bytes.Buffer) { 115 if k.parent != nil { 116 k.parent.marshal(b) 117 } 118 b.WriteByte('/') 119 b.WriteString(k.kind) 120 b.WriteByte(',') 121 if k.stringID != "" { 122 b.WriteString(k.stringID) 123 } else { 124 b.WriteString(strconv.FormatInt(k.intID, 10)) 125 } 126 } 127 128 // String returns a string representation of the key. 129 func (k *Key) String() string { 130 if k == nil { 131 return "" 132 } 133 b := bytes.NewBuffer(make([]byte, 0, 512)) 134 k.marshal(b) 135 return b.String() 136 } 137 138 type gobKey struct { 139 Kind string 140 StringID string 141 IntID int64 142 Parent *gobKey 143 AppID string 144 Namespace string 145 } 146 147 func keyToGobKey(k *Key) *gobKey { 148 if k == nil { 149 return nil 150 } 151 return &gobKey{ 152 Kind: k.kind, 153 StringID: k.stringID, 154 IntID: k.intID, 155 Parent: keyToGobKey(k.parent), 156 AppID: k.appID, 157 Namespace: k.namespace, 158 } 159 } 160 161 func gobKeyToKey(gk *gobKey) *Key { 162 if gk == nil { 163 return nil 164 } 165 return &Key{ 166 kind: gk.Kind, 167 stringID: gk.StringID, 168 intID: gk.IntID, 169 parent: gobKeyToKey(gk.Parent), 170 appID: gk.AppID, 171 namespace: gk.Namespace, 172 } 173 } 174 175 func (k *Key) GobEncode() ([]byte, error) { 176 buf := new(bytes.Buffer) 177 if err := gob.NewEncoder(buf).Encode(keyToGobKey(k)); err != nil { 178 return nil, err 179 } 180 return buf.Bytes(), nil 181 } 182 183 func (k *Key) GobDecode(buf []byte) error { 184 gk := new(gobKey) 185 if err := gob.NewDecoder(bytes.NewBuffer(buf)).Decode(gk); err != nil { 186 return err 187 } 188 *k = *gobKeyToKey(gk) 189 return nil 190 } 191 192 func (k *Key) MarshalJSON() ([]byte, error) { 193 return []byte(`"` + k.Encode() + `"`), nil 194 } 195 196 func (k *Key) UnmarshalJSON(buf []byte) error { 197 if len(buf) < 2 || buf[0] != '"' || buf[len(buf)-1] != '"' { 198 return errors.New("datastore: bad JSON key") 199 } 200 k2, err := DecodeKey(string(buf[1 : len(buf)-1])) 201 if err != nil { 202 return err 203 } 204 *k = *k2 205 return nil 206 } 207 208 // Encode returns an opaque representation of the key 209 // suitable for use in HTML and URLs. 210 // This is compatible with the Python and Java runtimes. 211 func (k *Key) Encode() string { 212 ref := keyToProto("", k) 213 214 b, err := proto.Marshal(ref) 215 if err != nil { 216 panic(err) 217 } 218 219 // Trailing padding is stripped. 220 return strings.TrimRight(base64.URLEncoding.EncodeToString(b), "=") 221 } 222 223 // DecodeKey decodes a key from the opaque representation returned by Encode. 224 func DecodeKey(encoded string) (*Key, error) { 225 // Re-add padding. 226 if m := len(encoded) % 4; m != 0 { 227 encoded += strings.Repeat("=", 4-m) 228 } 229 230 b, err := base64.URLEncoding.DecodeString(encoded) 231 if err != nil { 232 return nil, err 233 } 234 235 ref := new(pb.Reference) 236 if err := proto.Unmarshal(b, ref); err != nil { 237 return nil, err 238 } 239 240 return protoToKey(ref) 241 } 242 243 // NewIncompleteKey creates a new incomplete key. 244 // kind cannot be empty. 245 func NewIncompleteKey(c context.Context, kind string, parent *Key) *Key { 246 return NewKey(c, kind, "", 0, parent) 247 } 248 249 // NewKey creates a new key. 250 // kind cannot be empty. 251 // Either one or both of stringID and intID must be zero. If both are zero, 252 // the key returned is incomplete. 253 // parent must either be a complete key or nil. 254 func NewKey(c context.Context, kind, stringID string, intID int64, parent *Key) *Key { 255 // If there's a parent key, use its namespace. 256 // Otherwise, use any namespace attached to the context. 257 var namespace string 258 if parent != nil { 259 namespace = parent.namespace 260 } else { 261 namespace = internal.NamespaceFromContext(c) 262 } 263 264 return &Key{ 265 kind: kind, 266 stringID: stringID, 267 intID: intID, 268 parent: parent, 269 appID: internal.FullyQualifiedAppID(c), 270 namespace: namespace, 271 } 272 } 273 274 // AllocateIDs returns a range of n integer IDs with the given kind and parent 275 // combination. kind cannot be empty; parent may be nil. The IDs in the range 276 // returned will not be used by the datastore's automatic ID sequence generator 277 // and may be used with NewKey without conflict. 278 // 279 // The range is inclusive at the low end and exclusive at the high end. In 280 // other words, valid intIDs x satisfy low <= x && x < high. 281 // 282 // If no error is returned, low + n == high. 283 func AllocateIDs(c context.Context, kind string, parent *Key, n int) (low, high int64, err error) { 284 if kind == "" { 285 return 0, 0, errors.New("datastore: AllocateIDs given an empty kind") 286 } 287 if n < 0 { 288 return 0, 0, fmt.Errorf("datastore: AllocateIDs given a negative count: %d", n) 289 } 290 if n == 0 { 291 return 0, 0, nil 292 } 293 req := &pb.AllocateIdsRequest{ 294 ModelKey: keyToProto("", NewIncompleteKey(c, kind, parent)), 295 Size: proto.Int64(int64(n)), 296 } 297 res := &pb.AllocateIdsResponse{} 298 if err := internal.Call(c, "datastore_v3", "AllocateIds", req, res); err != nil { 299 return 0, 0, err 300 } 301 // The protobuf is inclusive at both ends. Idiomatic Go (e.g. slices, for loops) 302 // is inclusive at the low end and exclusive at the high end, so we add 1. 303 low = res.GetStart() 304 high = res.GetEnd() + 1 305 if low+int64(n) != high { 306 return 0, 0, fmt.Errorf("datastore: internal error: could not allocate %d IDs", n) 307 } 308 return low, high, nil 309 }