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
.common
.util
.Base64DecoderException
;
9 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.Reference
;
12 * This class enables direct creation of {@code Key} objects, both in
13 * the root entity group (no parent) and as the child of a given
14 * parent. Keys can also be created indirectly by putting named
15 * Entities into the datastore, which will allocate a new key. See
16 * {@link Entity#Entity(String, String)} for details.
18 * <p>This class also has methods for encoding and decoding
19 * {@code Key} objects to and from strings. Clients should not make
20 * any assumptions about the encoding format, except that it is a
21 * websafe string that does not need to be quoted when used in HTML or
26 public class KeyFactory
{
29 * Creates a new {@code Key} having no parent from its kind and ID.
31 * @param kind the kind of the key to create
32 * @param id the numeric identifier of the key in {@code kind}, unique
33 * across all root entities of this kind, must not be zero
35 public static Key
createKey(String kind
, long id
) {
36 return createKey(null, kind
, id
);
40 * Creates a new {@code Key} with the provided parent from its kind and ID.
42 * @param parent the parent of the key to create, can be {@code null}
43 * @param kind the kind of the key to create
44 * @param id the numeric identifier of the key in {@code kind}, unique
45 * across all entities of this kind with the same parent, must not be zero
47 public static Key
createKey(Key parent
, String kind
, long id
) {
48 return createKey(parent
, kind
, id
, null);
51 static Key
createKey(Key parent
, String kind
, long id
, AppIdNamespace appIdNamespace
) {
53 throw new IllegalArgumentException("id cannot be zero");
55 return new Key(kind
, parent
, id
, appIdNamespace
);
59 * Creates a new {@code Key} having no parent from its kind and name.
61 * @param kind the kind of the key to create
62 * @param name the name of the key in {@code kind}, as an arbitrary string
63 * unique across all root entities of this {@code kind}
65 public static Key
createKey(String kind
, String name
) {
66 return createKey(null, kind
, name
);
70 * Creates a new {@code Key} with the provided parent from its kind and name.
72 * @param parent the parent of the key to create, can be {@code null}
73 * @param kind the kind of the key to create
74 * @param name the name of the key in {@code kind}, as an arbitrary string
75 * unique across all entities of this {@code kind} with the same parent
77 public static Key
createKey(Key parent
, String kind
, String name
) {
78 return createKey(parent
, kind
, name
, null);
81 static Key
createKey(Key parent
, String kind
, String name
, AppIdNamespace appIdNamespace
) {
82 if (name
== null || name
.length() == 0) {
83 throw new IllegalArgumentException("name cannot be null or empty");
85 return new Key(kind
, parent
, name
, appIdNamespace
);
89 * Shorthand for invoking {@link #keyToString(Key)} on the result of
90 * {@link #createKey(String, long)}
92 * @param kind the kind of the key to create
93 * @param id the numeric identifier of the key in {@code kind}, unique
94 * across all root entities of this kind, must not be zero
95 * @return A websafe {@code String} representation of the {@link Key} that
98 public static String
createKeyString(String kind
, long id
) {
99 return keyToString(createKey(kind
, id
));
103 * Shorthand for invoking {@link #keyToString(Key)} on the result of
104 * {@link #createKey(Key, String, long)}
106 * @param parent the parent of the key to create, can be {@code null}.
107 * @param kind the kind of the key to create
108 * @param id the numeric identifier of the key in {@code kind}, unique
109 * across entities of this kind with the same parent, must not be zero
110 * @return A websafe {@code String} representation of the {@link Key} that
113 public static String
createKeyString(Key parent
, String kind
, long id
) {
114 return keyToString(createKey(parent
, kind
, id
));
118 * Shorthand for invoking {@link #keyToString(Key)} on the result of
119 * {@link #createKey(String, String)}
121 * @param kind the kind of the key to create
122 * @param name the name of the key in {@code kind}, as an arbitrary string
123 * unique across root entities of this {@code kind}
124 * @return A websafe {@code String} representation of the {@link Key} that
127 public static String
createKeyString(String kind
, String name
) {
128 return keyToString(createKey(kind
, name
));
132 * Shorthand for invoking {@link #keyToString(Key)} on the result of
133 * {@link #createKey(Key, String, String)}
135 * @param parent the parent of the key to create, can be {@code null}.
136 * @param kind the kind of the key to create
137 * @param name the name of the key in {@code kind}, as an arbitrary string
138 * unique across entities of this {@code kind} with the same parent
139 * @return A websafe {@code String} representation of the {@link Key} that
142 public static String
createKeyString(Key parent
, String kind
, String name
) {
143 return keyToString(createKey(parent
, kind
, name
));
147 * Converts a {@code Key} into a websafe string. For example, this string
148 * can safely be used as an URL parameter embedded in a HTML document.
151 * key.equals(KeyFactory.stringToKey(KeyFactory.keyToString(key))
153 * should evaluate to {@code true}.
155 * @param key The {@code Key} to convert to a {@code String}.
157 * @return A websafe {@code String} representation of the provided
160 * @throws IllegalArgumentException If the specified {@code Key}
163 public static String
keyToString(Key key
) {
164 if (!key
.isComplete()) {
165 throw new IllegalArgumentException("Key is incomplete.");
167 Reference reference
= KeyTranslator
.convertToPb(key
);
168 return base64Url().omitPadding().encode(reference
.toByteArray());
173 * Converts a {@code String}-representation of a {@code Key} into the
174 * {@code Key} instance it represents. Note that
176 * str.equals(KeyFactory.keyToString(KeyFactory.stringToKey(str))
178 * should evaluate to {@code true} for all strings returned by
179 * {@link KeyFactory#keyToString}.
181 * @param encoded The {@code String} representation of a {@code Key}.
183 * @return The {@code Key} that the given {@code String} represents.
185 * @throws IllegalArgumentException If the string cannot be parsed.
187 public static Key
stringToKey(String encoded
) {
188 int modulo
= encoded
.length() % 4;
190 encoded
+= "====".substring(modulo
);
193 byte[] encodedBytes
= encoded
.getBytes();
196 decodedBytes
= Base64
.decodeWebSafe(encodedBytes
, 0, encodedBytes
.length
);
197 } catch (Base64DecoderException ex
) {
198 throw new IllegalArgumentException("Cannot parse: " + encoded
, ex
);
201 Reference reference
= new Reference();
202 reference
.parseFrom(decodedBytes
);
203 return KeyTranslator
.createFromPb(reference
);
207 * Helper class that aids in the construction of {@link Key Keys} with
208 * ancestors. Initialize the {@code Builder} with the topmost ancestor
209 * in your key path and then add children using the {@link #addChild}
210 * overload that best suits your needs. When finished adding children,
211 * call {@link #getKey()} to retrieve your {@link Key} or
212 * {@link #getString()} to retrieve your {@link Key} encoded as a websafe
218 * import com.google.appengine.api.datastore.KeyFactory.Builder;
222 * Key key = new Builder("Person", 88).addChild("Address", 24).getKey();
223 * String keyStr = new Builder("Photo Album", "Vacation").addChild("Photo", 1424).getString();
226 public static final class Builder
{
230 * Create a {@code Builder}, establishing a {@link Key} constructed from
231 * the provided kind and name as the topmost ancestor.
233 * @param kind the kind of the topmost ancestor
234 * @param name the name of the topmost ancestor in {@code kind}, as an
235 * arbitrary string unique across root entities of this {@code kind}
237 public Builder(String kind
, String name
) {
238 current
= createKey(null, kind
, name
);
242 * Create a {@code Builder}, establishing a {@link Key} constructed from
243 * the provided kind and id as the topmost ancestor.
245 * @param kind the kind of the topmost ancestor
246 * @param id the numeric identifier of the topmost ancestor in {@code kind},
247 * unique across root entities of this kind, must not be zero
249 public Builder(String kind
, long id
) {
250 current
= createKey(null, kind
, id
);
254 * Create a {@code Builder}, establishing the provided {@link Key} as the
257 * @param key the topmost ancestor
259 public Builder(Key key
) {
264 * Add a {@link Key} constructed from the provided kind and name
265 * as the child of the {@link Key} most recently added to the
268 * @param kind the kind of the child
269 * @param name the name of the child in {@code kind}, as an arbitrary string
270 * unique across entities of this {@code kind} with the same parent
271 * @return {@code this}
273 public Builder
addChild(String kind
, String name
) {
274 current
= createKey(current
, kind
, name
);
279 * Add a {@link Key} constructed from the provided kind and id
280 * as the child of the {@link Key} most recently added to the
283 * @param kind the kind of the child
284 * @param id the numeric identifier of the child in {@code kind},
285 * unique across entities of this kind with the same parent, must not be
287 * @return {@code this}
289 public Builder
addChild(String kind
, long id
) {
290 current
= createKey(current
, kind
, id
);
295 * @return The most recently added {@link Key}.
297 public Key
getKey() {
302 * @return The most recently added {@link Key}, encoded as a websafe
305 public String
getString() {
306 return keyToString(current
);
310 private KeyFactory() {