1 // Copyright 2007 Google Inc. All rights reserved.
3 package com
.google
.appengine
.api
.datastore
;
5 import static com
.google
.common
.io
.BaseEncoding
.base64Url
;
7 import com
.google
.common
.util
.Base64
;
8 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.Reference
;
11 * This class enables direct creation of {@code Key} objects, both in
12 * the root entity group (no parent) and as the child of a given
13 * parent. Keys can also be created indirectly by putting named
14 * Entities into the datastore, which will allocate a new key. See
15 * {@link Entity#Entity(String, String)} for details.
17 * <p>This class also has methods for encoding and decoding
18 * {@code Key} objects to and from strings. Clients should not make
19 * any assumptions about the encoding format, except that it is a
20 * websafe string that does not need to be quoted when used in HTML or
25 public class KeyFactory
{
28 * Creates a new {@code Key} having no parent from its kind and ID.
30 * @param kind the kind of the key to create
31 * @param id the numeric identifier of the key in {@code kind}, unique
32 * across all root entities of this kind, must not be zero
34 public static Key
createKey(String kind
, long id
) {
35 return createKey(null, kind
, id
);
39 * Creates a new {@code Key} with the provided parent from its kind and ID.
41 * @param parent the parent of the key to create, can be {@code null}
42 * @param kind the kind of the key to create
43 * @param id the numeric identifier of the key in {@code kind}, unique
44 * across all entities of this kind with the same parent, must not be zero
46 public static Key
createKey(Key parent
, String kind
, long id
) {
47 return createKey(parent
, kind
, id
, null);
50 static Key
createKey(Key parent
, String kind
, long id
, AppIdNamespace appIdNamespace
) {
52 throw new IllegalArgumentException("id cannot be zero");
54 return new Key(kind
, parent
, id
, appIdNamespace
);
58 * Creates a new {@code Key} having no parent from its kind and name.
60 * @param kind the kind of the key to create
61 * @param name the name of the key in {@code kind}, as an arbitrary string
62 * unique across all root entities of this {@code kind}
64 public static Key
createKey(String kind
, String name
) {
65 return createKey(null, kind
, name
);
69 * Creates a new {@code Key} with the provided parent from its kind and name.
71 * @param parent the parent of the key to create, can be {@code null}
72 * @param kind the kind of the key to create
73 * @param name the name of the key in {@code kind}, as an arbitrary string
74 * unique across all entities of this {@code kind} with the same parent
76 public static Key
createKey(Key parent
, String kind
, String name
) {
77 return createKey(parent
, kind
, name
, null);
80 static Key
createKey(Key parent
, String kind
, String name
, AppIdNamespace appIdNamespace
) {
81 if (name
== null || name
.length() == 0) {
82 throw new IllegalArgumentException("name cannot be null or empty");
84 return new Key(kind
, parent
, name
, appIdNamespace
);
88 * Shorthand for invoking {@link #keyToString(Key)} on the result of
89 * {@link #createKey(String, long)}
91 * @param kind the kind of the key to create
92 * @param id the numeric identifier of the key in {@code kind}, unique
93 * across all root entities of this kind, must not be zero
94 * @return A websafe {@code String} representation of the {@link Key} that
97 public static String
createKeyString(String kind
, long id
) {
98 return keyToString(createKey(kind
, id
));
102 * Shorthand for invoking {@link #keyToString(Key)} on the result of
103 * {@link #createKey(Key, String, long)}
105 * @param parent the parent of the key to create, can be {@code null}.
106 * @param kind the kind of the key to create
107 * @param id the numeric identifier of the key in {@code kind}, unique
108 * across entities of this kind with the same parent, must not be zero
109 * @return A websafe {@code String} representation of the {@link Key} that
112 public static String
createKeyString(Key parent
, String kind
, long id
) {
113 return keyToString(createKey(parent
, kind
, id
));
117 * Shorthand for invoking {@link #keyToString(Key)} on the result of
118 * {@link #createKey(String, String)}
120 * @param kind the kind of the key to create
121 * @param name the name of the key in {@code kind}, as an arbitrary string
122 * unique across root entities of this {@code kind}
123 * @return A websafe {@code String} representation of the {@link Key} that
126 public static String
createKeyString(String kind
, String name
) {
127 return keyToString(createKey(kind
, name
));
131 * Shorthand for invoking {@link #keyToString(Key)} on the result of
132 * {@link #createKey(Key, String, String)}
134 * @param parent the parent of the key to create, can be {@code null}.
135 * @param kind the kind of the key to create
136 * @param name the name of the key in {@code kind}, as an arbitrary string
137 * unique across entities of this {@code kind} with the same parent
138 * @return A websafe {@code String} representation of the {@link Key} that
141 public static String
createKeyString(Key parent
, String kind
, String name
) {
142 return keyToString(createKey(parent
, kind
, name
));
146 * Converts a {@code Key} into a websafe string. For example, this string
147 * can safely be used as an URL parameter embedded in a HTML document.
150 * key.equals(KeyFactory.stringToKey(KeyFactory.keyToString(key))
152 * should evaluate to {@code true}.
154 * @param key The {@code Key} to convert to a {@code String}.
156 * @return A websafe {@code String} representation of the provided
159 * @throws IllegalArgumentException If the specified {@code Key}
162 public static String
keyToString(Key key
) {
163 if (!key
.isComplete()) {
164 throw new IllegalArgumentException("Key is incomplete.");
166 Reference reference
= KeyTranslator
.convertToPb(key
);
167 return base64Url().omitPadding().encode(reference
.toByteArray());
172 * Converts a {@code String}-representation of a {@code Key} into the
173 * {@code Key} instance it represents. Note that
175 * str.equals(KeyFactory.keyToString(KeyFactory.stringToKey(str))
177 * should evaluate to {@code true} for all strings returned by
178 * {@link KeyFactory#keyToString}.
180 * @param encoded The {@code String} representation of a {@code Key}.
182 * @return The {@code Key} that the given {@code String} represents.
184 * @throws IllegalArgumentException If the string cannot be parsed.
186 public static Key
stringToKey(String encoded
) {
187 int modulo
= encoded
.length() % 4;
189 encoded
+= "====".substring(modulo
);
192 byte[] encodedBytes
= encoded
.getBytes();
195 decodedBytes
= Base64
.decodeWebSafe(encodedBytes
, 0, encodedBytes
.length
);
196 } catch (IllegalArgumentException ex
) {
197 throw new IllegalArgumentException("Cannot parse: " + encoded
, ex
);
200 Reference reference
= new Reference();
201 reference
.parseFrom(decodedBytes
);
202 return KeyTranslator
.createFromPb(reference
);
206 * Helper class that aids in the construction of {@link Key Keys} with
207 * ancestors. Initialize the {@code Builder} with the topmost ancestor
208 * in your key path and then add children using the {@link #addChild}
209 * overload that best suits your needs. When finished adding children,
210 * call {@link #getKey()} to retrieve your {@link Key} or
211 * {@link #getString()} to retrieve your {@link Key} encoded as a websafe
217 * import com.google.appengine.api.datastore.KeyFactory.Builder;
221 * Key key = new Builder("Person", 88).addChild("Address", 24).getKey();
222 * String keyStr = new Builder("Photo Album", "Vacation").addChild("Photo", 1424).getString();
225 public static final class Builder
{
229 * Create a {@code Builder}, establishing a {@link Key} constructed from
230 * the provided kind and name as the topmost ancestor.
232 * @param kind the kind of the topmost ancestor
233 * @param name the name of the topmost ancestor in {@code kind}, as an
234 * arbitrary string unique across root entities of this {@code kind}
236 public Builder(String kind
, String name
) {
237 current
= createKey(null, kind
, name
);
241 * Create a {@code Builder}, establishing a {@link Key} constructed from
242 * the provided kind and id as the topmost ancestor.
244 * @param kind the kind of the topmost ancestor
245 * @param id the numeric identifier of the topmost ancestor in {@code kind},
246 * unique across root entities of this kind, must not be zero
248 public Builder(String kind
, long id
) {
249 current
= createKey(null, kind
, id
);
253 * Create a {@code Builder}, establishing the provided {@link Key} as the
256 * @param key the topmost ancestor
258 public Builder(Key key
) {
263 * Add a {@link Key} constructed from the provided kind and name
264 * as the child of the {@link Key} most recently added to the
267 * @param kind the kind of the child
268 * @param name the name of the child in {@code kind}, as an arbitrary string
269 * unique across entities of this {@code kind} with the same parent
270 * @return {@code this}
272 public Builder
addChild(String kind
, String name
) {
273 current
= createKey(current
, kind
, name
);
278 * Add a {@link Key} constructed from the provided kind and id
279 * as the child of the {@link Key} most recently added to the
282 * @param kind the kind of the child
283 * @param id the numeric identifier of the child in {@code kind},
284 * unique across entities of this kind with the same parent, must not be
286 * @return {@code this}
288 public Builder
addChild(String kind
, long id
) {
289 current
= createKey(current
, kind
, id
);
294 * @return The most recently added {@link Key}.
296 public Key
getKey() {
301 * @return The most recently added {@link Key}, encoded as a websafe
304 public String
getString() {
305 return keyToString(current
);
309 private KeyFactory() {