1 // Copyright 2007 Google Inc. All rights reserved.
3 package com
.google
.appengine
.api
.datastore
;
5 import com
.google
.appengine
.api
.blobstore
.BlobKey
;
6 import com
.google
.appengine
.api
.datastore
.Entity
.UnindexedValue
;
7 import com
.google
.appengine
.api
.datastore
.Entity
.WrappedValue
;
8 import com
.google
.appengine
.api
.datastore
.Entity
.WrappedValueImpl
;
9 import com
.google
.appengine
.api
.users
.User
;
10 import com
.google
.apphosting
.api
.ApiProxy
;
11 import com
.google
.common
.collect
.Lists
;
12 import com
.google
.common
.collect
.Maps
;
13 import com
.google
.datastore
.v1beta3
.EntityOrBuilder
;
14 import com
.google
.datastore
.v1beta3
.Key
.PathElement
;
15 import com
.google
.datastore
.v1beta3
.Key
.PathElement
.IdTypeCase
;
16 import com
.google
.datastore
.v1beta3
.KeyOrBuilder
;
17 import com
.google
.datastore
.v1beta3
.PartitionId
;
18 import com
.google
.datastore
.v1beta3
.PartitionIdOrBuilder
;
19 import com
.google
.datastore
.v1beta3
.Value
;
20 import com
.google
.datastore
.v1beta3
.Value
.NullValue
;
21 import com
.google
.datastore
.v1beta3
.Value
.ValueTypeCase
;
22 import com
.google
.datastore
.v1beta3
.ValueOrBuilder
;
23 import com
.google
.datastore
.v1beta3
.client
.DatastoreHelper
;
24 import com
.google
.io
.protocol
.ProtocolSupport
;
25 import com
.google
.protobuf
.ByteString
;
26 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.EntityProto
;
27 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.Path
;
28 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.Path
.Element
;
29 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.Property
;
30 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.Property
.Meaning
;
31 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.PropertyValue
;
32 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.PropertyValue
.ReferenceValue
;
33 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.PropertyValue
.ReferenceValuePathElement
;
34 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.PropertyValue
.UserValue
;
35 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.Reference
;
37 import java
.util
.ArrayList
;
38 import java
.util
.Arrays
;
39 import java
.util
.Collection
;
40 import java
.util
.Collections
;
41 import java
.util
.Date
;
42 import java
.util
.HashMap
;
43 import java
.util
.List
;
47 * {@code DataTypeTranslator} is a utility class for converting
48 * between the data store's {@code Property} protocol buffers and the
49 * user-facing classes ({@code String}, {@code User}, etc.).
52 public final class DataTypeTranslator
{
54 * This key points to an (optional) map from project id to app id in the
55 * {@link com.google.apphosting.api.ApiProxy.Environment} attribute map.
57 static final String ADDITIONAL_APP_IDS_MAP_ATTRIBUTE_KEY
=
58 "com.google.appengine.datastore.DataTypeTranslator.AdditionalAppIdsMap";
60 private static final RawValueType RAW_VALUE_TYPE
= new RawValueType();
62 * The list of supported types.
64 * Note: If you're going to modify this list, also update
65 * DataTypeUtils. We're not building {@link DataTypeUtils#getSupportedTypes}
66 * directly from this typeMap, because we want {@link DataTypeUtils} to be
67 * translatable by GWT, so that {@link Entity Entities} can be easily sent
68 * via GWT RPC. Also, if you add a type here that is not immutable you'll
69 * need to add special handling for it in {@link Entity#clone()}.
71 private static final Map
<Class
<?
>, Type
<?
>> typeMap
= Maps
.newHashMap();
73 typeMap
.put(RawValue
.class, RAW_VALUE_TYPE
);
75 typeMap
.put(Float
.class, new DoubleType());
76 typeMap
.put(Double
.class, new DoubleType());
78 typeMap
.put(Byte
.class, new Int64Type());
79 typeMap
.put(Short
.class, new Int64Type());
80 typeMap
.put(Integer
.class, new Int64Type());
81 typeMap
.put(Long
.class, new Int64Type());
82 typeMap
.put(Date
.class, new DateType());
83 typeMap
.put(Rating
.class, new RatingType());
85 typeMap
.put(String
.class, new StringType());
86 typeMap
.put(Link
.class, new LinkType());
87 typeMap
.put(ShortBlob
.class, new ShortBlobType());
88 typeMap
.put(Category
.class, new CategoryType());
89 typeMap
.put(PhoneNumber
.class, new PhoneNumberType());
90 typeMap
.put(PostalAddress
.class, new PostalAddressType());
91 typeMap
.put(Email
.class, new EmailType());
92 typeMap
.put(IMHandle
.class, new IMHandleType());
93 typeMap
.put(BlobKey
.class, new BlobKeyType());
94 typeMap
.put(Blob
.class, new BlobType());
95 typeMap
.put(Text
.class, new TextType());
96 typeMap
.put(EmbeddedEntity
.class, new EmbeddedEntityType());
98 typeMap
.put(Boolean
.class, new BoolType());
99 typeMap
.put(User
.class, new UserType());
100 typeMap
.put(Key
.class, new KeyType());
101 typeMap
.put(GeoPt
.class, new GeoPtType());
103 assert typeMap
.keySet().equals(DataTypeUtils
.getSupportedTypes())
104 : "Warning: DataTypeUtils and DataTypeTranslator do not agree "
105 + "about supported classes: " + typeMap
.keySet() + " vs. "
106 + DataTypeUtils
.getSupportedTypes();
110 * A map with the {@link Comparable} classes returned by all the instances of
111 * {@link Type#asComparable(Object)} as keys and the pb code point as the value.
112 * Used for comparing values that don't map to the same pb code point.
115 Map
<Class
<?
extends Comparable
<?
>>, Integer
> comparableTypeMap
=
116 new HashMap
<Class
<?
extends Comparable
<?
>>, Integer
>();
119 comparableTypeMap
.put(ComparableByteArray
.class, PropertyValue
.kstringValue
);
120 comparableTypeMap
.put(Long
.class, PropertyValue
.kint64Value
);
121 comparableTypeMap
.put(Double
.class, PropertyValue
.kdoubleValue
);
122 comparableTypeMap
.put(Boolean
.class, PropertyValue
.kbooleanValue
);
123 comparableTypeMap
.put(User
.class, PropertyValue
.kUserValueGroup
);
124 comparableTypeMap
.put(Key
.class, PropertyValue
.kReferenceValueGroup
);
125 comparableTypeMap
.put(GeoPt
.class, PropertyValue
.kPointValueGroup
);
129 * Add all of the properties in the specified map to an {@code EntityProto}.
130 * This involves determining the type of each property and creating the
131 * proper type-specific protocol buffer.
133 * If the property value is an {@link UnindexedValue}, or if it's a
134 * type that is never indexed, e.g. {@code Text} and {@code Blob}, it's
135 * added to {@code EntityProto.raw_property}. Otherwise it's added to
136 * {@code EntityProto.property}.
138 * @param map A not {@code null} map of all the properties which will
139 * be set on {@code proto}
140 * @param proto A not {@code null} protocol buffer
142 public static void addPropertiesToPb(Map
<String
, Object
> map
, EntityProto proto
) {
143 for (Map
.Entry
<String
, Object
> entry
: map
.entrySet()) {
144 String name
= entry
.getKey();
146 boolean forceIndexedEmbeddedEntity
= false;
147 boolean indexed
= true;
148 Object value
= entry
.getValue();
150 if (entry
.getValue() instanceof WrappedValue
) {
151 WrappedValue wrappedValue
= (WrappedValue
) entry
.getValue();
152 forceIndexedEmbeddedEntity
= wrappedValue
.getForceIndexedEmbeddedEntity();
153 indexed
= wrappedValue
.isIndexed();
154 value
= wrappedValue
.getValue();
157 if (value
instanceof Collection
<?
>) {
158 Collection
<?
> values
= (Collection
<?
>) value
;
159 if (values
.isEmpty()) {
160 addPropertyToPb(name
, null, indexed
, forceIndexedEmbeddedEntity
, false, proto
);
162 for (Object listValue
: values
) {
163 addPropertyToPb(name
, listValue
, indexed
, forceIndexedEmbeddedEntity
, true, proto
);
167 addPropertyToPb(name
, value
, indexed
, forceIndexedEmbeddedEntity
, false, proto
);
173 * Adds a property to {@code entity}.
175 * @param name the property name
176 * @param value the property value
177 * @param indexed whether this property should be indexed. This may be
178 * overriden by property types like Blob and Text that are never indexed.
179 * @param multiple whether this property has multiple values
180 * @param entity the entity to populate
182 private static void addPropertyToPb(String name
, Object value
, boolean indexed
,
183 boolean forceIndexedEmbeddedEntity
, boolean multiple
, EntityProto entity
) {
184 Property property
= new Property();
185 property
.setName(name
);
186 property
.setMultiple(multiple
);
187 PropertyValue newValue
= property
.getMutableValue();
189 Type
<?
> type
= getType(value
.getClass());
190 Meaning meaning
= type
.getV3Meaning();
191 if (meaning
!= property
.getMeaningEnum()) {
192 property
.setMeaning(meaning
);
194 boolean indexable
= type
.toV3Value(value
, newValue
);
195 if (!forceIndexedEmbeddedEntity
|| !(value
instanceof EmbeddedEntity
)) {
196 indexed
&= indexable
;
200 entity
.addRawProperty(property
);
202 entity
.addProperty(property
);
206 static PropertyValue
toV3Value(Object value
) {
207 PropertyValue propertyValue
= new PropertyValue();
209 getType(value
.getClass()).toV3Value(value
, propertyValue
);
211 return propertyValue
;
215 * Copy all of the indexed properties present on {@code proto} into {@code map}.
217 public static void extractIndexedPropertiesFromPb(EntityProto proto
, Map
<String
, Object
> map
) {
218 for (Property property
: proto
.propertys()) {
219 addPropertyToMap(property
, true, map
);
224 * Copy all of the unindexed properties present on {@code proto} into {@code map}.
226 private static void extractUnindexedPropertiesFromPb(EntityProto proto
, Map
<String
, Object
> map
) {
227 for (Property property
: proto
.rawPropertys()) {
228 addPropertyToMap(property
, false, map
);
233 * Copy all of the properties present on {@code proto} into {@code map}.
235 public static void extractPropertiesFromPb(EntityProto proto
, Map
<String
, Object
> map
) {
236 extractIndexedPropertiesFromPb(proto
, map
);
237 extractUnindexedPropertiesFromPb(proto
, map
);
241 * Copy all of the implicit properties present on {@code proto} into {@code map}.
243 public static void extractImplicitPropertiesFromPb(EntityProto proto
, Map
<String
, Object
> map
) {
244 for (Property property
: getImplicitProperties(proto
)) {
245 addPropertyToMap(property
, true, map
);
249 private static Iterable
<Property
> getImplicitProperties(EntityProto proto
) {
250 return Collections
.singleton(buildImplicitKeyProperty(proto
));
253 private static Property
buildImplicitKeyProperty(EntityProto proto
) {
254 Property keyProp
= new Property();
255 keyProp
.setName(Entity
.KEY_RESERVED_PROPERTY
);
256 PropertyValue propVal
= new PropertyValue();
257 propVal
.setReferenceValue(KeyType
.toReferenceValue(proto
.getKey()));
258 keyProp
.setValue(propVal
);
263 * Locates and returns all indexed properties with the given name on the
264 * given proto. If there are a mix of matching multiple and non-multiple
265 * properties, the collection will contain the first non-multiple property.
266 * @return A list, potentially empty, containing matching properties.
268 public static Collection
<Property
> findIndexedPropertiesOnPb(
269 EntityProto proto
, String propertyName
) {
270 if (propertyName
.equals(Entity
.KEY_RESERVED_PROPERTY
)) {
271 return Collections
.singleton(buildImplicitKeyProperty(proto
));
273 List
<Property
> matchingMultipleProps
= new ArrayList
<>();
274 for (Property prop
: proto
.propertys()) {
275 if (prop
.getName().equals(propertyName
)) {
276 if (!prop
.isMultiple()) {
277 return Collections
.singleton(prop
);
279 matchingMultipleProps
.add(prop
);
283 return matchingMultipleProps
;
286 private static void addPropertyToMap(Property property
, boolean indexed
,
287 Map
<String
, Object
> map
) {
288 String name
= property
.getName();
289 Object value
= getPropertyValue(property
);
291 if (property
.isMultiple()) {
292 @SuppressWarnings({"unchecked"})
293 List
<Object
> results
= (List
<Object
>) PropertyContainer
.unwrapValue(map
.get(name
));
294 if (results
== null) {
295 results
= new ArrayList
<Object
>();
296 map
.put(name
, indexed ? results
: new UnindexedValue(results
));
298 if (indexed
&& value
instanceof EmbeddedEntity
) {
299 map
.put(name
, new WrappedValueImpl(results
, true, true));
303 if (indexed
&& value
instanceof EmbeddedEntity
) {
304 value
= new WrappedValueImpl(value
, true, true);
305 } else if (!indexed
) {
306 value
= new UnindexedValue(value
);
308 map
.put(name
, value
);
313 * Returns the value for the property as its canonical type.
315 * @param property a not {@code null} property
316 * @return {@code null} if no value was set for {@code property}
318 public static Object
getPropertyValue(Property property
) {
319 PropertyValue value
= property
.getValue();
320 for (Type
<?
> type
: typeMap
.values()) {
321 if (type
.isType(property
.getMeaningEnum(), value
)) {
322 return type
.getValue(value
);
329 * @see #addPropertiesToPb(Map, EntityProto)
331 static void addPropertiesToPb(Map
<String
, Object
> map
,
332 com
.google
.datastore
.v1beta3
.Entity
.Builder proto
) {
333 for (Map
.Entry
<String
, Object
> entry
: map
.entrySet()) {
334 proto
.getMutableProperties().put(entry
.getKey(), toV1Value(entry
.getValue()).build());
339 * Copy all of the properties present on {@code proto} into {@code map}.
341 * <p>Cloud Datastore v1 entity must know if the proto came from an index-only query as the User
342 * type overwrites the INDEX_ONLY meaning.
344 * @param proto the proto from which to extract properties
345 * @param indexOnly if the proto is from an index only query (a projection query)
346 * @param map the map to populate
348 static void extractPropertiesFromPb(EntityOrBuilder proto
, boolean indexOnly
,
349 Map
<String
, Object
> map
) {
351 for (Map
.Entry
<String
, Value
> prop
: proto
.getProperties().entrySet()) {
352 map
.put(prop
.getKey(), new RawValue(prop
.getValue()));
355 for (Map
.Entry
<String
, Value
> prop
: proto
.getProperties().entrySet()) {
356 addPropertyToMap(prop
.getKey(), prop
.getValue(), map
);
361 static Value
.Builder
toV1Value(
362 Object value
, boolean indexed
, boolean forceIndexedEmbeddedEntity
) {
364 Value
.Builder builder
= Value
.newBuilder();
365 builder
.setNullValue(NullValue
.NULL_VALUE
);
366 builder
.setExcludeFromIndexes(!indexed
);
369 return getType(value
.getClass()).toV1Value(value
, indexed
, forceIndexedEmbeddedEntity
);
372 private static Value
.Builder
toV1Value(Object value
) {
373 boolean indexed
= true;
374 boolean forceIndexedEmbeddedEntity
= false;
376 if (value
instanceof WrappedValue
) {
377 WrappedValue wrappedValue
= (WrappedValue
) value
;
378 indexed
= wrappedValue
.isIndexed();
379 forceIndexedEmbeddedEntity
= wrappedValue
.getForceIndexedEmbeddedEntity();
380 value
= wrappedValue
.getValue();
383 if (value
instanceof Collection
<?
>) {
384 Collection
<?
> values
= (Collection
<?
>) value
;
385 if (values
.isEmpty()) {
386 return toV1Value(null, indexed
, forceIndexedEmbeddedEntity
);
388 Value
.Builder valueBuilder
= Value
.newBuilder();
389 for (Object listValue
: values
) {
390 valueBuilder
.getArrayValueBuilder().addValues(
391 toV1Value(listValue
, indexed
, forceIndexedEmbeddedEntity
));
396 return toV1Value(value
, indexed
, forceIndexedEmbeddedEntity
);
400 private static void addPropertyToMap(String name
, Value value
, Map
<String
, Object
> map
) {
401 boolean isOrContainsIndexedEntityValue
= false;
402 boolean indexed
= false;
405 if (value
.getArrayValue().getValuesCount() > 0) {
406 ArrayList
<Object
> resultList
=
407 new ArrayList
<Object
>(value
.getArrayValue().getValuesCount());
408 for (Value subValue
: value
.getArrayValue().getValuesList()) {
409 if (subValue
.getValueTypeCase() == ValueTypeCase
.ARRAY_VALUE
) {
410 throw new IllegalArgumentException("Invalid Entity PB: list within a list.");
412 result
= getValue(subValue
);
413 if (!subValue
.getExcludeFromIndexes()) {
415 if (result
instanceof EmbeddedEntity
) {
416 isOrContainsIndexedEntityValue
= true;
419 resultList
.add(result
);
423 indexed
= !value
.getExcludeFromIndexes();
424 result
= getValue(value
);
425 if (indexed
&& result
instanceof EmbeddedEntity
) {
426 isOrContainsIndexedEntityValue
= true;
430 if (isOrContainsIndexedEntityValue
) {
431 result
= new WrappedValueImpl(result
, true, true);
432 } else if (!indexed
) {
433 result
= new UnindexedValue(result
);
436 map
.put(name
, result
);
439 private static Object
getValue(Value value
) {
440 for (Type
<?
> type
: typeMap
.values()) {
441 if (type
.isType(value
)) {
442 return type
.getValue(value
);
448 private static Meaning
getV3MeaningOf(ValueOrBuilder value
) {
449 return Meaning
.valueOf(value
.getMeaning());
452 private static AppIdNamespace
toAppIdNamespace(PartitionIdOrBuilder partitionId
) {
453 if (partitionId
.getProjectId().equals(DatastoreApiHelper
.getCurrentProjectId())) {
454 return new AppIdNamespace(DatastoreApiHelper
.getCurrentAppId(), partitionId
.getNamespaceId());
456 Map
<String
, String
> additionalProjectIdToAppIdMap
= getAdditionalProjectIdToAppIdMap();
457 if (additionalProjectIdToAppIdMap
.containsKey(partitionId
.getProjectId())) {
458 return new AppIdNamespace(additionalProjectIdToAppIdMap
.get(partitionId
.getProjectId()),
459 partitionId
.getNamespaceId());
461 throw new IllegalStateException(String
.format(
462 "Could not determine app id corresponding to project id \"%s\". Please add the app id "
464 partitionId
.getProjectId(), RemoteCloudDatastoreV1Proxy
.ADDITIONAL_APP_IDS_VAR
));
468 @SuppressWarnings("unchecked")
469 private static Map
<String
, String
> getAdditionalProjectIdToAppIdMap() {
470 if (ApiProxy
.getCurrentEnvironment() != null) {
471 Object attribute
= ApiProxy
.getCurrentEnvironment().getAttributes()
472 .get(ADDITIONAL_APP_IDS_MAP_ATTRIBUTE_KEY
);
473 if (attribute
!= null) {
474 return (Map
<String
, String
>) attribute
;
477 return Collections
.emptyMap();
480 private static PartitionId
.Builder
toV1PartitionId(AppIdNamespace appNs
) {
481 PartitionId
.Builder builder
= PartitionId
.newBuilder();
482 builder
.setProjectId(DatastoreApiHelper
.toProjectId(appNs
.getAppId()));
483 if (!appNs
.getNamespace().isEmpty()) {
484 builder
.setNamespaceId(appNs
.getNamespace());
489 static com
.google
.datastore
.v1beta3
.Key
.Builder
toV1Key(Key key
) {
490 com
.google
.datastore
.v1beta3
.Key
.Builder builder
=
491 com
.google
.datastore
.v1beta3
.Key
.newBuilder();
492 builder
.setPartitionId(toV1PartitionId(key
.getAppIdNamespace()));
493 List
<PathElement
> pathElementList
= new ArrayList
<>();
495 PathElement
.Builder pathElement
= PathElement
.newBuilder();
496 pathElement
.setKind(key
.getKind());
497 if (key
.getName() != null) {
498 pathElement
.setName(key
.getName());
499 } else if (key
.getId() != Key
.NOT_ASSIGNED
) {
500 pathElement
.setId(key
.getId());
502 pathElementList
.add(pathElement
.build());
503 key
= key
.getParent();
504 } while (key
!= null);
505 builder
.addAllPath(Lists
.reverse(pathElementList
));
509 static Key
toKey(KeyOrBuilder proto
) {
510 if (proto
.getPathCount() == 0) {
511 throw new IllegalArgumentException("Invalid Key PB: no elements.");
513 AppIdNamespace appIdNamespace
= toAppIdNamespace(proto
.getPartitionId());
515 for (PathElement e
: proto
.getPathList()) {
516 String kind
= e
.getKind();
517 key
= new Key(kind
, key
, e
.getId(),
518 e
.getIdTypeCase() == IdTypeCase
.NAME ? e
.getName() : null, appIdNamespace
);
523 static Entity
toEntity(EntityOrBuilder entityV1
) {
524 Entity entity
= new Entity(DataTypeTranslator
.toKey(entityV1
.getKey()));
525 DataTypeTranslator
.extractPropertiesFromPb(entityV1
, false,
526 entity
.getPropertyMap());
530 static Entity
toEntity(EntityOrBuilder entityV1
, Collection
<Projection
> projections
) {
531 Entity entity
= new Entity(DataTypeTranslator
.toKey(entityV1
.getKey()));
533 Map
<String
, Object
> values
= Maps
.newHashMap();
534 DataTypeTranslator
.extractPropertiesFromPb(entityV1
, true, values
);
535 for (Projection projection
: projections
) {
536 entity
.setProperty(projection
.getName(), projection
.getValue(values
));
541 static com
.google
.datastore
.v1beta3
.Entity
.Builder
toV1Entity(Entity entity
) {
542 com
.google
.datastore
.v1beta3
.Entity
.Builder entityV1
=
543 com
.google
.datastore
.v1beta3
.Entity
.newBuilder();
544 entityV1
.setKey(toV1Key(entity
.getKey()));
545 addPropertiesToPb(entity
.getPropertyMap(), entityV1
);
550 * Returns the value for the property as its comparable representation type.
552 * @param property a not {@code null} property
553 * @return {@code null} if no value was set for {@code property}
555 @SuppressWarnings("unchecked")
556 public static Comparable
<Object
> getComparablePropertyValue(Property property
) {
557 return (Comparable
<Object
>) RAW_VALUE_TYPE
.asComparable(new RawValue(property
.getValue()));
561 * Converts the given {@link Object} into a supported value then returns it as
562 * a comparable object so it can be compared to other data types.
564 * @param value any Object that can be converted into a supported DataType
565 * @return {@code null} if value is null
566 * @throws UnsupportedOperationException if value is not supported
568 @SuppressWarnings("unchecked")
569 static Comparable
<Object
> getComparablePropertyValue(Object value
) {
570 return value
== null ?
null
571 : (Comparable
<Object
>) getType(value
.getClass()).asComparable(value
);
575 * Get the rank of the given datastore type relative to other datastore
576 * types. Note that datastore types do not necessarily have unique ranks.
578 @SuppressWarnings({"unchecked", "rawtypes"})
579 public static int getTypeRank(Class
<?
extends Comparable
> datastoreType
) {
580 return comparableTypeMap
.get(datastoreType
);
584 * Gets the {@link Type} that knows how to translate objects of
585 * type {@code clazz} into protocol buffers that the data store can
587 * @throws UnsupportedOperationException if clazz is not supported
589 @SuppressWarnings("unchecked")
590 private static <T
> Type
<T
> getType(Class
<T
> clazz
) {
591 if (typeMap
.containsKey(clazz
)) {
592 return (Type
<T
>) typeMap
.get(clazz
);
594 throw new UnsupportedOperationException("Unsupported data type: " + clazz
.getName());
599 * {@code Type} is an abstract class that knows how to convert Java
600 * objects of one or more types into datastore representations.
602 * @param <T> The canonical Java class for this type.
604 abstract static class Type
<T
> {
606 * @return {@code true} if the given meaning and property value matches this {@link Type}.
608 public final boolean isType(Meaning meaning
, PropertyValue propertyValue
) {
609 return meaning
== getV3Meaning() && hasValue(propertyValue
);
613 * @return {@code true} if the given value matches this {@link Type}.
615 public boolean isType(Value propertyValue
) {
616 return getV3MeaningOf(propertyValue
) == getV3Meaning() && hasValue(propertyValue
);
620 * Returns the {@link Comparable} for the given value, or
621 * {@code null} if values of this type are not comparable.
623 public abstract Comparable
<?
> asComparable(Object value
);
626 * Sets the value of {@code propertyValue} to {@code value}.
627 * @return if the value is indexable
629 public abstract boolean toV3Value(Object value
, PropertyValue propertyValue
);
632 * Returns a new Cloud Datastore v1 Value for the given parameters.
634 * @param value the Java native value to convert
635 * @param indexed the desired indexing, ignored for types that are not indexable
636 * @return the Cloud Datastore v1 representation of the given value and desired indexing
638 public abstract Value
.Builder
toV1Value(
639 Object value
, boolean indexed
, boolean forceIndexedEmbeddedEntity
);
642 * Returns the value of {@code propertyValue} as its canonical Java type.
644 * Use {@link #isType} first to determine if the property has a value of the given type.
646 * @param propertyValue a not {@code null} value representing this {@code Type}
647 * @return the canonical Java representation of {@code propertyValue}.
649 public abstract T
getValue(PropertyValue propertyValue
);
652 * @see Type#getValue(PropertyValue)
654 public abstract T
getValue(Value value
);
657 * @return {@code true} if a value of this {@code Type} is set on the given propertyValue.
659 public abstract boolean hasValue(PropertyValue propertyValue
);
662 * @return {@code true} if a value of this {@code Type} is set on the given propertyValue.
664 public abstract boolean hasValue(Value propertyValue
);
667 * @return the {@link Meaning} for this {@link Type}
669 protected Meaning
getV3Meaning() {
670 return Meaning
.NO_MEANING
;
675 * A base class with common functions for types that have the same datastore representation.
677 * @param <S> the datastore type
678 * @param <T> the canonical Java class for this type
680 private abstract static class BaseVariantType
<S
, T
> extends Type
<T
> {
682 * @return the datastore representation of the given value
684 protected abstract S
toDatastoreValue(Object value
);
686 * @return the native representation of the given value
688 protected abstract T
fromDatastoreValue(S datastoreValue
);
692 * Base class for types that store strings in the datastore.
694 * @param <T> the canonical Java class for this type
696 private abstract static class BaseStringType
<T
> extends BaseVariantType
<String
, T
> {
698 public boolean toV3Value(Object value
, PropertyValue propertyValue
) {
699 propertyValue
.setStringValue(toDatastoreValue(value
));
704 public Value
.Builder
toV1Value(
705 Object value
, boolean indexed
, boolean forceIndexedEmbeddedEntity
) {
706 Value
.Builder builder
= Value
.newBuilder();
707 builder
.setStringValue(toDatastoreValue(value
));
708 builder
.setExcludeFromIndexes(!indexed
);
709 builder
.setMeaning(getV3Meaning().getValue());
714 public final T
getValue(PropertyValue propertyValue
) {
715 return fromDatastoreValue(propertyValue
.getStringValue());
719 public T
getValue(Value propertyValue
) {
720 return fromDatastoreValue(propertyValue
.getStringValue());
724 public final boolean hasValue(PropertyValue propertyValue
) {
725 return propertyValue
.hasStringValue();
729 public boolean hasValue(Value propertyValue
) {
730 return propertyValue
.getValueTypeCase() == ValueTypeCase
.STRING_VALUE
;
734 public ComparableByteArray
asComparable(Object value
) {
735 return new ComparableByteArray(ProtocolSupport
.toBytesUtf8(toDatastoreValue(value
)));
740 * Base class for types that store bytes in the datastore.
742 * @param <T> the canonical Java class for this type
744 private abstract static class BaseBlobType
<T
> extends BaseVariantType
<byte[], T
> {
745 protected abstract boolean isIndexable();
748 public final boolean hasValue(PropertyValue propertyValue
) {
749 return propertyValue
.hasStringValue();
753 public final boolean toV3Value(Object value
, PropertyValue propertyValue
) {
754 propertyValue
.setStringValueAsBytes(toDatastoreValue(value
));
755 return isIndexable();
759 public Value
.Builder
toV1Value(
760 Object value
, boolean indexed
, boolean forceIndexedEmbeddedEntity
) {
761 Value
.Builder builder
= Value
.newBuilder();
762 builder
.setBlobValue(ByteString
.copyFrom(toDatastoreValue(value
)));
763 builder
.setExcludeFromIndexes(!indexed
|| !isIndexable());
768 public final T
getValue(PropertyValue propertyValue
) {
769 return fromDatastoreValue(propertyValue
.getStringValueAsBytes());
773 public final ComparableByteArray
asComparable(Object value
) {
774 return isIndexable() ?
new ComparableByteArray(toDatastoreValue(value
)) : null;
779 * Base class for types that store predefined entities in Cloud Datastore v1.
781 * @param <T> the canonical Java class for this type
783 private abstract static class BasePredefinedEntityType
<T
> extends Type
<T
> {
785 * @return the predefined entity meaning to use in Cloud Datastore v1
787 protected abstract int getV1Meaning();
790 * @return the Cloud Datastore v1 Entity representation for the given value
792 protected abstract com
.google
.datastore
.v1beta3
.Entity
getEntity(Object value
);
795 public final boolean isType(Value propertyValue
) {
796 return propertyValue
.getMeaning() == getV1Meaning() && hasValue(propertyValue
);
800 public final boolean hasValue(Value propertyValue
) {
801 return propertyValue
.getValueTypeCase() == ValueTypeCase
.ENTITY_VALUE
;
805 public final Value
.Builder
toV1Value(
806 Object value
, boolean indexed
, boolean forceIndexedEmbeddedEntity
) {
807 Value
.Builder builder
= Value
.newBuilder();
808 builder
.setEntityValue(getEntity(value
));
809 builder
.setExcludeFromIndexes(!indexed
);
810 builder
.setMeaning(getV1Meaning());
816 * Returns the Cloud Datastore v1 value representation for the given value, unindexed.
818 private static Value
makeUnindexedValue(String value
) {
819 return Value
.newBuilder().setStringValue(value
).setExcludeFromIndexes(true).build();
823 * Base class for types that int64 values in the datastore.
825 * @param <T> the canonical Java class for this type
827 private abstract static class BaseInt64Type
<T
> extends BaseVariantType
<Long
, T
> {
829 public final boolean toV3Value(Object value
, PropertyValue propertyValue
) {
830 propertyValue
.setInt64Value(toDatastoreValue(value
));
835 public Value
.Builder
toV1Value(
836 Object value
, boolean indexed
, boolean forceIndexedEmbeddedEntity
) {
837 Value
.Builder builder
= Value
.newBuilder();
838 builder
.setIntegerValue(toDatastoreValue(value
));
839 builder
.setExcludeFromIndexes(!indexed
);
840 builder
.setMeaning(getV3Meaning().getValue());
845 public T
getValue(PropertyValue propertyValue
) {
846 return fromDatastoreValue(propertyValue
.getInt64Value());
850 public T
getValue(Value propertyValue
) {
851 return fromDatastoreValue(propertyValue
.getIntegerValue());
855 public boolean hasValue(PropertyValue propertyValue
) {
856 return propertyValue
.hasInt64Value();
860 public boolean hasValue(Value propertyValue
) {
861 return propertyValue
.getValueTypeCase() == ValueTypeCase
.INTEGER_VALUE
;
865 public Long
asComparable(Object value
) {
866 return toDatastoreValue(value
);
871 * The type for projected index values.
873 private static final class RawValueType
extends Type
<RawValue
> {
875 public Meaning
getV3Meaning() {
876 return Meaning
.INDEX_VALUE
;
880 public boolean hasValue(PropertyValue propertyValue
) {
885 public boolean hasValue(Value propertyValue
) {
890 public boolean toV3Value(Object value
, PropertyValue propertyValue
) {
891 throw new UnsupportedOperationException();
895 public Value
.Builder
toV1Value(
896 Object value
, boolean indexed
, boolean forceIndexedEmbeddedEntity
) {
897 throw new UnsupportedOperationException();
901 public RawValue
getValue(PropertyValue propertyValue
) {
902 return new RawValue(propertyValue
);
906 public RawValue
getValue(Value propertyValue
) {
907 return new RawValue(propertyValue
);
910 @SuppressWarnings("unchecked")
912 public Comparable
<?
> asComparable(Object value
) {
913 value
= ((RawValue
) value
).getValue();
914 if (value
instanceof byte[]) {
915 return new ComparableByteArray((byte[]) value
);
917 return (Comparable
<?
>) value
;
922 * The raw String type.
924 private static final class StringType
extends BaseStringType
<String
> {
926 protected String
toDatastoreValue(Object value
) {
927 return value
.toString();
931 protected String
fromDatastoreValue(String datastoreValue
) {
932 return datastoreValue
;
937 * The raw int64 type.
939 private static final class Int64Type
extends BaseInt64Type
<Long
> {
941 protected Long
toDatastoreValue(Object value
) {
942 return ((Number
) value
).longValue();
946 protected Long
fromDatastoreValue(Long datastoreValue
) {
947 return datastoreValue
;
952 * The raw double type.
954 private static final class DoubleType
extends Type
<Double
> {
956 public boolean toV3Value(Object value
, PropertyValue propertyValue
) {
957 propertyValue
.setDoubleValue(((Number
) value
).doubleValue());
962 public Value
.Builder
toV1Value(
963 Object value
, boolean indexed
, boolean forceIndexedEmbeddedEntity
) {
964 Value
.Builder builder
= Value
.newBuilder();
965 builder
.setDoubleValue(((Number
) value
).doubleValue());
966 builder
.setExcludeFromIndexes(!indexed
);
971 public Double
getValue(PropertyValue propertyValue
) {
972 return propertyValue
.getDoubleValue();
976 public Double
getValue(Value propertyValue
) {
977 return propertyValue
.getDoubleValue();
981 public boolean hasValue(PropertyValue propertyValue
) {
982 return propertyValue
.hasDoubleValue();
986 public boolean hasValue(Value propertyValue
) {
987 return propertyValue
.getValueTypeCase() == ValueTypeCase
.DOUBLE_VALUE
;
991 public Double
asComparable(Object value
) {
992 return ((Number
) value
).doubleValue();
997 * The raw boolean type.
999 private static final class BoolType
extends Type
<Boolean
> {
1001 public boolean toV3Value(Object value
, PropertyValue propertyValue
) {
1002 propertyValue
.setBooleanValue((Boolean
) value
);
1007 public Value
.Builder
toV1Value(
1008 Object value
, boolean indexed
, boolean forceIndexedEmbeddedEntity
) {
1009 Value
.Builder builder
= Value
.newBuilder();
1010 builder
.setBooleanValue((Boolean
) value
);
1011 builder
.setExcludeFromIndexes(!indexed
);
1016 public Boolean
getValue(PropertyValue propertyValue
) {
1017 return propertyValue
.isBooleanValue();
1021 public Boolean
getValue(Value propertyValue
) {
1022 return propertyValue
.getBooleanValue();
1026 public boolean hasValue(PropertyValue propertyValue
) {
1027 return propertyValue
.hasBooleanValue();
1031 public boolean hasValue(Value propertyValue
) {
1032 return propertyValue
.getValueTypeCase() == ValueTypeCase
.BOOLEAN_VALUE
;
1036 public Boolean
asComparable(Object value
) {
1037 return (Boolean
) value
;
1044 * <p>Stored as an entity with a special meaning in v1.
1046 private static final class UserType
extends BasePredefinedEntityType
<User
> {
1047 public static final int MEANING_PREDEFINED_ENTITY_USER
= 20;
1048 public static final String PROPERTY_NAME_EMAIL
= "email";
1049 public static final String PROPERTY_NAME_AUTH_DOMAIN
= "auth_domain";
1050 public static final String PROPERTY_NAME_USER_ID
= "user_id";
1053 public int getV1Meaning() {
1054 return MEANING_PREDEFINED_ENTITY_USER
;
1058 public com
.google
.datastore
.v1beta3
.Entity
getEntity(Object value
) {
1059 User user
= (User
) value
;
1060 com
.google
.datastore
.v1beta3
.Entity
.Builder builder
=
1061 com
.google
.datastore
.v1beta3
.Entity
.newBuilder();
1062 builder
.getMutableProperties().put(PROPERTY_NAME_EMAIL
, makeUnindexedValue(user
.getEmail()));
1063 builder
.getMutableProperties().put(PROPERTY_NAME_AUTH_DOMAIN
,
1064 makeUnindexedValue(user
.getAuthDomain()));
1065 if (user
.getUserId() != null) {
1066 builder
.getMutableProperties().put(PROPERTY_NAME_USER_ID
,
1067 makeUnindexedValue(user
.getUserId()));
1069 return builder
.build();
1073 public boolean toV3Value(Object value
, PropertyValue propertyValue
) {
1074 User user
= (User
) value
;
1075 UserValue userValue
= new UserValue();
1076 userValue
.setEmail(user
.getEmail());
1077 userValue
.setAuthDomain(user
.getAuthDomain());
1078 if (user
.getUserId() != null) {
1079 userValue
.setObfuscatedGaiaid(user
.getUserId());
1081 userValue
.setGaiaid(0);
1082 propertyValue
.setUserValue(userValue
);
1087 public User
getValue(PropertyValue propertyValue
) {
1088 UserValue userValue
= propertyValue
.getUserValue();
1089 String userId
= userValue
.hasObfuscatedGaiaid() ? userValue
.getObfuscatedGaiaid() : null;
1090 return new User(userValue
.getEmail(), userValue
.getAuthDomain(), userId
);
1094 public User
getValue(Value propertyValue
) {
1096 String authDomain
= "";
1097 String userId
= null;
1098 for (Map
.Entry
<String
, Value
> prop
1099 : propertyValue
.getEntityValueOrBuilder().getProperties().entrySet()) {
1100 if (prop
.getKey().equals(PROPERTY_NAME_EMAIL
)) {
1101 email
= prop
.getValue().getStringValue();
1102 } else if (prop
.getKey().equals(PROPERTY_NAME_AUTH_DOMAIN
)) {
1103 authDomain
= prop
.getValue().getStringValue();
1104 } else if (prop
.getKey().equals(PROPERTY_NAME_USER_ID
)) {
1105 userId
= prop
.getValue().getStringValue();
1108 return new User(email
, authDomain
, userId
);
1112 public boolean hasValue(PropertyValue propertyValue
) {
1113 return propertyValue
.hasUserValue();
1117 public final Comparable
<User
> asComparable(Object value
) {
1118 return (User
) value
;
1125 * <p>Stored as a GeoPoint value with no meaning in Cloud Datastore v1.
1127 private static class GeoPtType
extends Type
<GeoPt
> {
1129 public boolean isType(Value propertyValue
) {
1130 return propertyValue
.getValueTypeCase() == ValueTypeCase
.GEO_POINT_VALUE
1131 && propertyValue
.getMeaning() == 0;
1135 public boolean toV3Value(Object value
, PropertyValue propertyValue
) {
1136 GeoPt geoPt
= (GeoPt
) value
;
1137 PropertyValue
.PointValue pv
= new PropertyValue
.PointValue()
1138 .setX(geoPt
.getLatitude())
1139 .setY(geoPt
.getLongitude());
1140 propertyValue
.setPointValue(pv
);
1145 public final Value
.Builder
toV1Value(
1146 Object value
, boolean indexed
, boolean forceIndexedEmbeddedEntity
) {
1147 GeoPt geoPt
= (GeoPt
) value
;
1148 Value
.Builder builder
= Value
.newBuilder();
1149 builder
.getGeoPointValueBuilder()
1150 .setLatitude(geoPt
.getLatitude())
1151 .setLongitude(geoPt
.getLongitude());
1152 builder
.setExcludeFromIndexes(!indexed
);
1157 public GeoPt
getValue(PropertyValue propertyValue
) {
1158 PropertyValue
.PointValue pv
= propertyValue
.getPointValue();
1159 return new GeoPt((float) pv
.getX(), (float) pv
.getY());
1163 public GeoPt
getValue(Value propertyValue
) {
1165 (float) propertyValue
.getGeoPointValue().getLatitude(),
1166 (float) propertyValue
.getGeoPointValue().getLongitude());
1170 public boolean hasValue(PropertyValue propertyValue
) {
1171 return propertyValue
.hasPointValue();
1175 public final boolean hasValue(Value propertyValue
) {
1176 return propertyValue
.getValueTypeCase() == ValueTypeCase
.GEO_POINT_VALUE
;
1180 public Meaning
getV3Meaning() {
1181 return Meaning
.GEORSS_POINT
;
1185 public final Comparable
<GeoPt
> asComparable(Object value
) {
1186 return (GeoPt
) value
;
1191 * The key/reference type.
1193 private static final class KeyType
extends Type
<Key
> {
1195 public boolean toV3Value(Object value
, PropertyValue propertyValue
) {
1196 Reference keyRef
= KeyTranslator
.convertToPb((Key
) value
);
1197 propertyValue
.setReferenceValue(toReferenceValue(keyRef
));
1202 public Value
.Builder
toV1Value(
1203 Object value
, boolean indexed
, boolean forceIndexedEmbeddedEntity
) {
1204 Value
.Builder builder
= Value
.newBuilder();
1205 builder
.setKeyValue(toV1Key((Key
) value
));
1206 builder
.setExcludeFromIndexes(!indexed
);
1211 public Key
getValue(PropertyValue propertyValue
) {
1212 return KeyTranslator
.createFromPb(toReference(propertyValue
.getReferenceValue()));
1216 public Key
getValue(Value propertyValue
) {
1217 return toKey(propertyValue
.getKeyValue());
1221 public boolean hasValue(PropertyValue propertyValue
) {
1222 return propertyValue
.hasReferenceValue();
1226 public boolean hasValue(Value propertyValue
) {
1227 return propertyValue
.getValueTypeCase() == ValueTypeCase
.KEY_VALUE
;
1231 public Key
asComparable(Object value
) {
1235 private static ReferenceValue
toReferenceValue(Reference keyRef
) {
1236 ReferenceValue refValue
= new ReferenceValue();
1237 refValue
.setApp(keyRef
.getApp());
1238 if (keyRef
.hasNameSpace()) {
1239 refValue
.setNameSpace(keyRef
.getNameSpace());
1241 Path path
= keyRef
.getPath();
1242 for (Element element
: path
.elements()) {
1243 ReferenceValuePathElement newElement
= new ReferenceValuePathElement();
1244 newElement
.setType(element
.getType());
1245 if (element
.hasName()) {
1246 newElement
.setName(element
.getName());
1248 if (element
.hasId()) {
1249 newElement
.setId(element
.getId());
1251 refValue
.addPathElement(newElement
);
1257 private static Reference
toReference(ReferenceValue refValue
) {
1258 Reference reference
= new Reference();
1259 reference
.setApp(refValue
.getApp());
1260 if (refValue
.hasNameSpace()) {
1261 reference
.setNameSpace(refValue
.getNameSpace());
1263 Path path
= new Path();
1264 for (ReferenceValuePathElement element
: refValue
.pathElements()) {
1265 Element newElement
= new Element();
1266 newElement
.setType(element
.getType());
1267 if (element
.hasName()) {
1268 newElement
.setName(element
.getName());
1270 if (element
.hasId()) {
1271 newElement
.setId(element
.getId());
1273 path
.addElement(newElement
);
1275 reference
.setPath(path
);
1281 * The non-indexable blob type.
1283 private static class BlobType
extends BaseBlobType
<Blob
> {
1285 public Meaning
getV3Meaning() {
1286 return Meaning
.BLOB
;
1290 public boolean isType(Value propertyValue
) {
1291 return getV3MeaningOf(propertyValue
) == Meaning
.NO_MEANING
1292 && propertyValue
.getExcludeFromIndexes()
1293 && hasValue(propertyValue
);
1297 public boolean hasValue(Value propertyValue
) {
1298 return propertyValue
.getValueTypeCase() == ValueTypeCase
.BLOB_VALUE
;
1302 public Blob
getValue(Value propertyValue
) {
1303 return fromDatastoreValue(propertyValue
.getBlobValue().toByteArray());
1307 protected Blob
fromDatastoreValue(byte[] datastoreValue
) {
1308 return new Blob(datastoreValue
);
1312 protected byte[] toDatastoreValue(Object value
) {
1313 return ((Blob
) value
).getBytes();
1317 public boolean isIndexable() {
1323 * The indexable blob type.
1325 private static class ShortBlobType
extends BaseBlobType
<ShortBlob
> {
1327 public Meaning
getV3Meaning() {
1328 return Meaning
.BYTESTRING
;
1332 public boolean isType(Value propertyValue
) {
1333 if (!hasValue(propertyValue
)) {
1337 if (propertyValue
.getExcludeFromIndexes()) {
1338 return getV3MeaningOf(propertyValue
) == getV3Meaning();
1340 return getV3MeaningOf(propertyValue
) == Meaning
.NO_MEANING
;
1345 public Value
.Builder
toV1Value(
1346 Object value
, boolean indexed
, boolean forceIndexedEmbeddedEntity
) {
1347 Value
.Builder builder
= super.toV1Value(value
, indexed
, forceIndexedEmbeddedEntity
);
1349 builder
.setMeaning(getV3Meaning().getValue());
1355 public boolean hasValue(Value propertyValue
) {
1356 return propertyValue
.getValueTypeCase() == ValueTypeCase
.BLOB_VALUE
1357 || (getV3MeaningOf(propertyValue
) == Meaning
.INDEX_VALUE
1358 && propertyValue
.getValueTypeCase() == ValueTypeCase
.STRING_VALUE
);
1362 public ShortBlob
getValue(Value propertyValue
) {
1363 if (getV3MeaningOf(propertyValue
) == Meaning
.INDEX_VALUE
1364 && propertyValue
.getValueTypeCase() == ValueTypeCase
.STRING_VALUE
) {
1365 return fromDatastoreValue(propertyValue
.getStringValueBytes().toByteArray());
1367 return fromDatastoreValue(propertyValue
.getBlobValue().toByteArray());
1372 protected byte[] toDatastoreValue(Object value
) {
1373 return ((ShortBlob
) value
).getBytes();
1377 protected ShortBlob
fromDatastoreValue(byte[] datastoreValue
) {
1378 return new ShortBlob(datastoreValue
);
1382 public boolean isIndexable() {
1390 * Stored as a partially serialized EntityProto in V3.
1392 private static final class EmbeddedEntityType
extends Type
<EmbeddedEntity
> {
1394 public Meaning
getV3Meaning() {
1395 return Meaning
.ENTITY_PROTO
;
1399 public boolean isType(Value propertyValue
) {
1400 return getV3MeaningOf(propertyValue
) == Meaning
.NO_MEANING
1401 && hasValue(propertyValue
);
1405 public boolean hasValue(PropertyValue propertyValue
) {
1406 return propertyValue
.hasStringValue();
1410 public boolean hasValue(Value propertyValue
) {
1411 return propertyValue
.getValueTypeCase() == ValueTypeCase
.ENTITY_VALUE
;
1415 public EmbeddedEntity
getValue(PropertyValue propertyValue
) {
1416 EntityProto proto
= new EntityProto();
1417 proto
.mergeFrom(propertyValue
.getStringValueAsBytes());
1418 EmbeddedEntity result
= new EmbeddedEntity();
1419 if (proto
.hasKey() && !proto
.getKey().getApp().isEmpty()) {
1420 result
.setKey(KeyTranslator
.createFromPb(proto
.getKey()));
1422 extractPropertiesFromPb(proto
, result
.getPropertyMap());
1427 public EmbeddedEntity
getValue(Value propertyValue
) {
1428 EmbeddedEntity result
= new EmbeddedEntity();
1429 com
.google
.datastore
.v1beta3
.Entity proto
= propertyValue
.getEntityValue();
1430 if (proto
.hasKey()) {
1431 result
.setKey(toKey(proto
.getKey()));
1433 extractPropertiesFromPb(proto
, false, result
.getPropertyMap());
1438 public boolean toV3Value(Object value
, PropertyValue propertyValue
) {
1439 EmbeddedEntity structProp
= (EmbeddedEntity
) value
;
1440 EntityProto proto
= new EntityProto();
1441 if (structProp
.getKey() != null) {
1442 proto
.setKey(KeyTranslator
.convertToPb(structProp
.getKey()));
1444 addPropertiesToPb(structProp
.getPropertyMap(), proto
);
1445 propertyValue
.setStringValueAsBytes(proto
.toByteArray());
1450 public Value
.Builder
toV1Value(
1451 Object value
, boolean indexed
, boolean forceIndexedEmbeddedEntity
) {
1452 EmbeddedEntity structProp
= (EmbeddedEntity
) value
;
1453 Value
.Builder builder
= Value
.newBuilder();
1454 com
.google
.datastore
.v1beta3
.Entity
.Builder proto
= builder
.getEntityValueBuilder();
1455 if (structProp
.getKey() != null) {
1456 proto
.setKey(toV1Key(structProp
.getKey()));
1458 addPropertiesToPb(structProp
.getPropertyMap(), proto
);
1459 builder
.setExcludeFromIndexes(!indexed
|| !forceIndexedEmbeddedEntity
);
1464 public Comparable
<?
> asComparable(Object value
) {
1470 * The non-indexable {@link Text} type.
1472 private static final class TextType
extends BaseStringType
<Text
> {
1474 public Meaning
getV3Meaning() {
1475 return Meaning
.TEXT
;
1479 public boolean toV3Value(Object value
, PropertyValue propertyValue
) {
1480 super.toV3Value(value
, propertyValue
);
1485 public Value
.Builder
toV1Value(
1486 Object value
, boolean indexed
, boolean forceIndexedEmbeddedEntity
) {
1487 return super.toV1Value(value
, false, false);
1491 protected Text
fromDatastoreValue(String datastoreString
) {
1492 return new Text(datastoreString
);
1496 protected String
toDatastoreValue(Object value
) {
1497 return ((Text
) value
).getValue();
1501 public ComparableByteArray
asComparable(Object value
) {
1507 * The {@link BlobKey} type. Blob keys are just strings with a special meaning.
1509 private static final class BlobKeyType
extends BaseStringType
<BlobKey
> {
1511 public Meaning
getV3Meaning() {
1512 return Meaning
.BLOBKEY
;
1516 public Value
.Builder
toV1Value(
1517 Object value
, boolean indexed
, boolean forceIndexedEmbeddedEntity
) {
1518 Value
.Builder builder
= Value
.newBuilder();
1519 builder
.setStringValue(toDatastoreValue(value
));
1520 builder
.setMeaning(Meaning
.BLOBKEY
.getValue());
1521 builder
.setExcludeFromIndexes(!indexed
);
1526 public BlobKey
getValue(Value propertyValue
) {
1527 return fromDatastoreValue(propertyValue
.getStringValue());
1531 protected String
toDatastoreValue(Object value
) {
1532 return ((BlobKey
) value
).getKeyString();
1536 protected BlobKey
fromDatastoreValue(String datastoreString
) {
1537 return new BlobKey(datastoreString
);
1544 * In V3 dates are just int64s with a special meaning.
1546 private static final class DateType
extends BaseInt64Type
<Date
> {
1548 public Meaning
getV3Meaning() {
1549 return Meaning
.GD_WHEN
;
1553 public boolean isType(Value propertyValue
) {
1554 return propertyValue
.getMeaning() == 0 && hasValue(propertyValue
);
1558 public boolean hasValue(Value propertyValue
) {
1559 return propertyValue
.getValueTypeCase() == ValueTypeCase
.TIMESTAMP_VALUE
1560 || (getV3MeaningOf(propertyValue
) == Meaning
.INDEX_VALUE
1561 && propertyValue
.getValueTypeCase() == ValueTypeCase
.INTEGER_VALUE
);
1565 public Value
.Builder
toV1Value(
1566 Object value
, boolean indexed
, boolean forceIndexedEmbeddedEntity
) {
1567 Value
.Builder builder
= DatastoreHelper
.makeValue((Date
) value
);
1568 builder
.setExcludeFromIndexes(!indexed
);
1573 public Date
getValue(Value propertyValue
) {
1574 if (getV3MeaningOf(propertyValue
) == Meaning
.INDEX_VALUE
1575 && propertyValue
.getValueTypeCase() == ValueTypeCase
.INTEGER_VALUE
) {
1576 return fromDatastoreValue(propertyValue
.getIntegerValue());
1578 long datastoreValue
= DatastoreHelper
.getTimestamp(propertyValue
);
1579 return fromDatastoreValue(datastoreValue
);
1584 protected Long
toDatastoreValue(Object value
) {
1585 return ((Date
) value
).getTime() * 1000L;
1589 protected Date
fromDatastoreValue(Long datastoreValue
) {
1590 return new Date(datastoreValue
/ 1000L);
1595 * Internally a link is just a string with a special meaning.
1597 private static final class LinkType
extends BaseStringType
<Link
> {
1599 public Meaning
getV3Meaning() {
1600 return Meaning
.ATOM_LINK
;
1604 protected String
toDatastoreValue(Object value
) {
1605 return ((Link
) value
).getValue();
1609 protected Link
fromDatastoreValue(String datastoreValue
) {
1610 return new Link(datastoreValue
);
1615 * Internally a category is just a string with a special meaning.
1617 private static final class CategoryType
extends BaseStringType
<Category
> {
1619 public Meaning
getV3Meaning() {
1620 return Meaning
.ATOM_CATEGORY
;
1624 protected String
toDatastoreValue(Object value
) {
1625 return ((Category
) value
).getCategory();
1629 protected Category
fromDatastoreValue(String datastoreString
) {
1630 return new Category(datastoreString
);
1635 * Internally a rating is just an int64 with a special meaning.
1637 private static final class RatingType
extends BaseInt64Type
<Rating
> {
1639 public Meaning
getV3Meaning() {
1640 return Meaning
.GD_RATING
;
1644 protected Long
toDatastoreValue(Object value
) {
1645 return (long) ((Rating
) value
).getRating();
1649 protected Rating
fromDatastoreValue(Long datastoreLong
) {
1650 return new Rating(datastoreLong
.intValue());
1655 * Internally an email is just a string with a special meaning.
1657 private static final class EmailType
extends BaseStringType
<Email
> {
1659 public Meaning
getV3Meaning() {
1660 return Meaning
.GD_EMAIL
;
1664 protected String
toDatastoreValue(Object value
) {
1665 return ((Email
) value
).getEmail();
1669 protected Email
fromDatastoreValue(String datastoreString
) {
1670 return new Email(datastoreString
);
1675 * Internally a postal address is just a string with a special meaning.
1677 private static final class PostalAddressType
extends BaseStringType
<PostalAddress
> {
1679 public Meaning
getV3Meaning() {
1680 return Meaning
.GD_POSTALADDRESS
;
1684 protected String
toDatastoreValue(Object value
) {
1685 return ((PostalAddress
) value
).getAddress();
1689 protected PostalAddress
fromDatastoreValue(String datastoreString
) {
1690 return new PostalAddress(datastoreString
);
1695 * Internally a phone number is just a string with a special meaning.
1697 private static final class PhoneNumberType
extends BaseStringType
<PhoneNumber
> {
1699 public Meaning
getV3Meaning() {
1700 return Meaning
.GD_PHONENUMBER
;
1704 protected String
toDatastoreValue(Object value
) {
1705 return ((PhoneNumber
) value
).getNumber();
1709 protected PhoneNumber
fromDatastoreValue(String datastoreString
) {
1710 return new PhoneNumber(datastoreString
);
1715 * Internally an IM handle is just a string with a special meaning and a
1716 * well known format.
1718 private static final class IMHandleType
extends BaseStringType
<IMHandle
> {
1720 public Meaning
getV3Meaning() {
1721 return Meaning
.GD_IM
;
1725 protected String
toDatastoreValue(Object value
) {
1726 return ((IMHandle
) value
).toDatastoreString();
1730 protected IMHandle
fromDatastoreValue(String datastoreString
) {
1731 return IMHandle
.fromDatastoreString(datastoreString
);
1735 static Map
<Class
<?
>, Type
<?
>> getTypeMap() {
1740 * A wrapper for a {@code byte[]} that implements {@link Comparable}.
1741 * Comparison algorithm is the same as the prod datastore.
1743 public static final class ComparableByteArray
implements Comparable
<ComparableByteArray
> {
1744 private final byte[] bytes
;
1746 public ComparableByteArray(byte[] bytes
) {
1751 public int compareTo(ComparableByteArray other
) {
1752 byte[] otherBytes
= other
.bytes
;
1753 for (int i
= 0; i
< Math
.min(bytes
.length
, otherBytes
.length
); i
++) {
1754 int v1
= bytes
[i
] & 0xFF;
1755 int v2
= otherBytes
[i
] & 0xFF;
1760 return bytes
.length
- otherBytes
.length
;
1764 public boolean equals(Object obj
) {
1768 return Arrays
.equals(bytes
, ((ComparableByteArray
) obj
).bytes
);
1772 public int hashCode() {
1774 for (byte b
: bytes
) {
1775 result
= 31 * result
+ b
;
1781 private DataTypeTranslator() {