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
.ArrayValue
;
14 import com
.google
.datastore
.v1beta3
.EntityOrBuilder
;
15 import com
.google
.datastore
.v1beta3
.Key
.PathElement
;
16 import com
.google
.datastore
.v1beta3
.Key
.PathElement
.IdTypeCase
;
17 import com
.google
.datastore
.v1beta3
.KeyOrBuilder
;
18 import com
.google
.datastore
.v1beta3
.PartitionId
;
19 import com
.google
.datastore
.v1beta3
.PartitionIdOrBuilder
;
20 import com
.google
.datastore
.v1beta3
.Value
;
21 import com
.google
.datastore
.v1beta3
.Value
.NullValue
;
22 import com
.google
.datastore
.v1beta3
.Value
.ValueTypeCase
;
23 import com
.google
.datastore
.v1beta3
.ValueOrBuilder
;
24 import com
.google
.datastore
.v1beta3
.client
.DatastoreHelper
;
25 import com
.google
.io
.protocol
.ProtocolSupport
;
26 import com
.google
.protobuf
.ByteString
;
27 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.EntityProto
;
28 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.Path
;
29 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.Path
.Element
;
30 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.Property
;
31 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.Property
.Meaning
;
32 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.PropertyValue
;
33 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.PropertyValue
.ReferenceValue
;
34 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.PropertyValue
.ReferenceValuePathElement
;
35 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.PropertyValue
.UserValue
;
36 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.Reference
;
38 import java
.util
.ArrayList
;
39 import java
.util
.Arrays
;
40 import java
.util
.Collection
;
41 import java
.util
.Collections
;
42 import java
.util
.Date
;
43 import java
.util
.HashMap
;
44 import java
.util
.List
;
48 * {@code DataTypeTranslator} is a utility class for converting
49 * between the data store's {@code Property} protocol buffers and the
50 * user-facing classes ({@code String}, {@code User}, etc.).
53 public final class DataTypeTranslator
{
55 * This key points to an (optional) map from project id to app id in the
56 * {@link com.google.apphosting.api.ApiProxy.Environment} attribute map.
58 static final String ADDITIONAL_APP_IDS_MAP_ATTRIBUTE_KEY
=
59 "com.google.appengine.datastore.DataTypeTranslator.AdditionalAppIdsMap";
61 private static final RawValueType RAW_VALUE_TYPE
= new RawValueType();
63 * The list of supported types.
65 * Note: If you're going to modify this list, also update
66 * DataTypeUtils. We're not building {@link DataTypeUtils#getSupportedTypes}
67 * directly from this typeMap, because we want {@link DataTypeUtils} to be
68 * translatable by GWT, so that {@link Entity Entities} can be easily sent
69 * via GWT RPC. Also, if you add a type here that is not immutable you'll
70 * need to add special handling for it in {@link Entity#clone()}.
72 private static final Map
<Class
<?
>, Type
<?
>> typeMap
= Maps
.newHashMap();
74 typeMap
.put(RawValue
.class, RAW_VALUE_TYPE
);
76 typeMap
.put(Float
.class, new DoubleType());
77 typeMap
.put(Double
.class, new DoubleType());
79 typeMap
.put(Byte
.class, new Int64Type());
80 typeMap
.put(Short
.class, new Int64Type());
81 typeMap
.put(Integer
.class, new Int64Type());
82 typeMap
.put(Long
.class, new Int64Type());
83 typeMap
.put(Date
.class, new DateType());
84 typeMap
.put(Rating
.class, new RatingType());
86 typeMap
.put(String
.class, new StringType());
87 typeMap
.put(Link
.class, new LinkType());
88 typeMap
.put(ShortBlob
.class, new ShortBlobType());
89 typeMap
.put(Category
.class, new CategoryType());
90 typeMap
.put(PhoneNumber
.class, new PhoneNumberType());
91 typeMap
.put(PostalAddress
.class, new PostalAddressType());
92 typeMap
.put(Email
.class, new EmailType());
93 typeMap
.put(IMHandle
.class, new IMHandleType());
94 typeMap
.put(BlobKey
.class, new BlobKeyType());
95 typeMap
.put(Blob
.class, new BlobType());
96 typeMap
.put(Text
.class, new TextType());
97 typeMap
.put(EmbeddedEntity
.class, new EmbeddedEntityType());
99 typeMap
.put(Boolean
.class, new BoolType());
100 typeMap
.put(User
.class, new UserType());
101 typeMap
.put(Key
.class, new KeyType());
102 typeMap
.put(GeoPt
.class, new GeoPtType());
104 assert typeMap
.keySet().equals(DataTypeUtils
.getSupportedTypes())
105 : "Warning: DataTypeUtils and DataTypeTranslator do not agree "
106 + "about supported classes: " + typeMap
.keySet() + " vs. "
107 + DataTypeUtils
.getSupportedTypes();
111 * A map with the {@link Comparable} classes returned by all the instances of
112 * {@link Type#asComparable(Object)} as keys and the pb code point as the value.
113 * Used for comparing values that don't map to the same pb code point.
116 Map
<Class
<?
extends Comparable
<?
>>, Integer
> comparableTypeMap
=
117 new HashMap
<Class
<?
extends Comparable
<?
>>, Integer
>();
120 comparableTypeMap
.put(ComparableByteArray
.class, PropertyValue
.kstringValue
);
121 comparableTypeMap
.put(Long
.class, PropertyValue
.kint64Value
);
122 comparableTypeMap
.put(Double
.class, PropertyValue
.kdoubleValue
);
123 comparableTypeMap
.put(Boolean
.class, PropertyValue
.kbooleanValue
);
124 comparableTypeMap
.put(User
.class, PropertyValue
.kUserValueGroup
);
125 comparableTypeMap
.put(Key
.class, PropertyValue
.kReferenceValueGroup
);
126 comparableTypeMap
.put(GeoPt
.class, PropertyValue
.kPointValueGroup
);
130 * Add all of the properties in the specified map to an {@code EntityProto}.
131 * This involves determining the type of each property and creating the
132 * proper type-specific protocol buffer.
134 * If the property value is an {@link UnindexedValue}, or if it's a
135 * type that is never indexed, e.g. {@code Text} and {@code Blob}, it's
136 * added to {@code EntityProto.raw_property}. Otherwise it's added to
137 * {@code EntityProto.property}.
139 * @param map A not {@code null} map of all the properties which will
140 * be set on {@code proto}
141 * @param proto A not {@code null} protocol buffer
143 public static void addPropertiesToPb(Map
<String
, Object
> map
, EntityProto proto
) {
144 for (Map
.Entry
<String
, Object
> entry
: map
.entrySet()) {
145 String name
= entry
.getKey();
147 boolean forceIndexedEmbeddedEntity
= false;
148 boolean indexed
= true;
149 Object value
= entry
.getValue();
151 if (entry
.getValue() instanceof WrappedValue
) {
152 WrappedValue wrappedValue
= (WrappedValue
) entry
.getValue();
153 forceIndexedEmbeddedEntity
= wrappedValue
.getForceIndexedEmbeddedEntity();
154 indexed
= wrappedValue
.isIndexed();
155 value
= wrappedValue
.getValue();
158 if (value
instanceof Collection
<?
>) {
159 Collection
<?
> values
= (Collection
<?
>) value
;
160 addListPropertyToPb(proto
, name
, indexed
, values
, forceIndexedEmbeddedEntity
);
162 addPropertyToPb(name
, value
, indexed
, forceIndexedEmbeddedEntity
, false, proto
);
167 private static void addListPropertyToPb(EntityProto proto
, String name
, boolean indexed
,
168 Collection
<?
> values
, boolean forceIndexedEmbeddedEntity
) {
169 if (values
.isEmpty()) {
170 Property property
= new Property();
171 property
.setName(name
);
172 property
.setMultiple(false);
173 if (DatastoreServiceConfig
.getEmptyListSupport()) {
174 property
.setMeaning(Meaning
.EMPTY_LIST
);
177 property
.getMutableValue();
179 proto
.addProperty(property
);
181 proto
.addRawProperty(property
);
184 for (Object listValue
: values
) {
185 addPropertyToPb(name
, listValue
, indexed
, forceIndexedEmbeddedEntity
,
192 * Adds a property to {@code entity}.
194 * @param name the property name
195 * @param value the property value
196 * @param indexed whether this property should be indexed. This may be
197 * overridden by property types like Blob and Text that are never indexed.
198 * @param multiple whether this property has multiple values
199 * @param entity the entity to populate
201 private static void addPropertyToPb(String name
, Object value
, boolean indexed
,
202 boolean forceIndexedEmbeddedEntity
, boolean multiple
, EntityProto entity
) {
203 Property property
= new Property();
204 property
.setName(name
);
205 property
.setMultiple(multiple
);
206 PropertyValue newValue
= property
.getMutableValue();
208 Type
<?
> type
= getType(value
.getClass());
209 Meaning meaning
= type
.getV3Meaning();
210 if (meaning
!= property
.getMeaningEnum()) {
211 property
.setMeaning(meaning
);
213 type
.toV3Value(value
, newValue
);
214 if (!forceIndexedEmbeddedEntity
|| !(value
instanceof EmbeddedEntity
)) {
215 indexed
&= type
.canBeIndexed();
219 entity
.addProperty(property
);
221 entity
.addRawProperty(property
);
225 static PropertyValue
toV3Value(Object value
) {
226 PropertyValue propertyValue
= new PropertyValue();
228 getType(value
.getClass()).toV3Value(value
, propertyValue
);
230 return propertyValue
;
234 * Copy all of the indexed properties present on {@code proto} into {@code map}.
236 public static void extractIndexedPropertiesFromPb(EntityProto proto
, Map
<String
, Object
> map
) {
237 for (Property property
: proto
.propertys()) {
238 addPropertyToMap(property
, true, map
);
243 * Copy all of the unindexed properties present on {@code proto} into {@code map}.
245 private static void extractUnindexedPropertiesFromPb(EntityProto proto
, Map
<String
, Object
> map
) {
246 for (Property property
: proto
.rawPropertys()) {
247 addPropertyToMap(property
, false, map
);
252 * Copy all of the properties present on {@code proto} into {@code map}.
254 public static void extractPropertiesFromPb(EntityProto proto
, Map
<String
, Object
> map
) {
255 extractIndexedPropertiesFromPb(proto
, map
);
256 extractUnindexedPropertiesFromPb(proto
, map
);
260 * Copy all of the implicit properties present on {@code proto} into {@code map}.
262 public static void extractImplicitPropertiesFromPb(EntityProto proto
, Map
<String
, Object
> map
) {
263 for (Property property
: getImplicitProperties(proto
)) {
264 addPropertyToMap(property
, true, map
);
268 private static Iterable
<Property
> getImplicitProperties(EntityProto proto
) {
269 return Collections
.singleton(buildImplicitKeyProperty(proto
));
272 private static Property
buildImplicitKeyProperty(EntityProto proto
) {
273 Property keyProp
= new Property();
274 keyProp
.setName(Entity
.KEY_RESERVED_PROPERTY
);
275 PropertyValue propVal
= new PropertyValue();
276 propVal
.setReferenceValue(KeyType
.toReferenceValue(proto
.getKey()));
277 keyProp
.setValue(propVal
);
282 * Locates and returns all indexed properties with the given name on the
283 * given proto. If there are a mix of matching multiple and non-multiple
284 * properties, the collection will contain the first non-multiple property.
285 * @return A list, potentially empty, containing matching properties.
287 public static Collection
<Property
> findIndexedPropertiesOnPb(
288 EntityProto proto
, String propertyName
) {
289 if (propertyName
.equals(Entity
.KEY_RESERVED_PROPERTY
)) {
290 return Collections
.singleton(buildImplicitKeyProperty(proto
));
292 List
<Property
> matchingMultipleProps
= new ArrayList
<>();
293 for (Property prop
: proto
.propertys()) {
294 if (prop
.getName().equals(propertyName
)) {
295 if (!prop
.isMultiple()) {
296 return Collections
.singleton(prop
);
298 matchingMultipleProps
.add(prop
);
302 return matchingMultipleProps
;
305 private static Object
wrapIfUnindexed(boolean indexed
, Object value
) {
306 return indexed ? value
: new UnindexedValue(value
);
309 private static void addPropertyToMap(Property property
, boolean indexed
,
310 Map
<String
, Object
> map
) {
311 String name
= property
.getName();
313 if (property
.getMeaningEnum() == Meaning
.EMPTY_LIST
) {
314 Object emptyListValue
=
315 DatastoreServiceConfig
.getEmptyListSupport() ?
new ArrayList
<Object
>() : null;
316 map
.put(name
, wrapIfUnindexed(indexed
, emptyListValue
));
318 Object value
= getPropertyValue(property
);
319 if (property
.isMultiple()) {
320 @SuppressWarnings({"unchecked"})
321 List
<Object
> resultList
= (List
<Object
>) PropertyContainer
.unwrapValue(map
.get(name
));
322 if (resultList
== null) {
323 resultList
= new ArrayList
<Object
>();
324 map
.put(name
, indexed ? resultList
: new UnindexedValue(resultList
));
326 if (indexed
&& value
instanceof EmbeddedEntity
) {
327 map
.put(name
, new WrappedValueImpl(resultList
, true, true));
329 resultList
.add(value
);
331 if (indexed
&& value
instanceof EmbeddedEntity
) {
332 value
= new WrappedValueImpl(value
, true, true);
333 } else if (!indexed
) {
334 value
= new UnindexedValue(value
);
336 map
.put(name
, value
);
342 * Returns the value for the property as its canonical type.
344 * @param property a not {@code null} property
345 * @return {@code null} if no value was set for {@code property}
347 public static Object
getPropertyValue(Property property
) {
348 PropertyValue value
= property
.getValue();
349 for (Type
<?
> type
: typeMap
.values()) {
350 if (type
.isType(property
.getMeaningEnum(), value
)) {
351 return type
.getValue(value
);
358 * @see #addPropertiesToPb(Map, EntityProto)
360 static void addPropertiesToPb(Map
<String
, Object
> map
,
361 com
.google
.datastore
.v1beta3
.Entity
.Builder proto
) {
362 for (Map
.Entry
<String
, Object
> entry
: map
.entrySet()) {
363 proto
.getMutableProperties().put(entry
.getKey(), toV1Value(entry
.getValue()).build());
368 * Copy all of the properties present on {@code proto} into {@code map}.
370 * <p>Cloud Datastore v1 entity must know if the proto came from an index-only query as the User
371 * type overwrites the INDEX_ONLY meaning.
373 * @param proto the proto from which to extract properties
374 * @param indexOnly if the proto is from an index only query (a projection query)
375 * @param map the map to populate
377 static void extractPropertiesFromPb(EntityOrBuilder proto
, boolean indexOnly
,
378 Map
<String
, Object
> map
) {
380 for (Map
.Entry
<String
, Value
> prop
: proto
.getProperties().entrySet()) {
381 map
.put(prop
.getKey(), new RawValue(prop
.getValue()));
384 for (Map
.Entry
<String
, Value
> prop
: proto
.getProperties().entrySet()) {
385 addPropertyToMap(prop
.getKey(), prop
.getValue(), map
);
390 static Value
.Builder
toV1Value(
391 Object value
, boolean indexed
, boolean forceIndexedEmbeddedEntity
) {
393 Value
.Builder builder
= Value
.newBuilder();
394 builder
.setNullValue(NullValue
.NULL_VALUE
);
395 builder
.setExcludeFromIndexes(!indexed
);
398 return getType(value
.getClass()).toV1Value(value
, indexed
, forceIndexedEmbeddedEntity
);
401 private static Value
.Builder
toV1Value(Object value
) {
402 boolean indexed
= true;
403 boolean forceIndexedEmbeddedEntity
= false;
405 if (value
instanceof WrappedValue
) {
406 WrappedValue wrappedValue
= (WrappedValue
) value
;
407 indexed
= wrappedValue
.isIndexed();
408 forceIndexedEmbeddedEntity
= wrappedValue
.getForceIndexedEmbeddedEntity();
409 value
= wrappedValue
.getValue();
412 if (value
instanceof Collection
<?
>) {
413 Collection
<?
> values
= (Collection
<?
>) value
;
414 if (values
.isEmpty()) {
415 if (DatastoreServiceConfig
.getEmptyListSupport()) {
416 return Value
.newBuilder().setExcludeFromIndexes(!indexed
).setArrayValue(ArrayValue
.newBuilder());
418 return toV1Value(null, indexed
, forceIndexedEmbeddedEntity
);
421 Value
.Builder valueBuilder
= Value
.newBuilder();
422 for (Object listValue
: values
) {
423 valueBuilder
.getArrayValueBuilder().addValues(
424 toV1Value(listValue
, indexed
, forceIndexedEmbeddedEntity
));
429 return toV1Value(value
, indexed
, forceIndexedEmbeddedEntity
);
433 private static void addPropertyToMap(String name
, Value value
, Map
<String
, Object
> map
) {
434 boolean isOrContainsIndexedEntityValue
= false;
438 if (DatastoreServiceConfig
.getEmptyListSupport()
439 && value
.getValueTypeCase() == Value
.ValueTypeCase
.ARRAY_VALUE
440 && value
.getArrayValue().getValuesCount() == 0) {
441 result
= new ArrayList
<Object
>();
442 indexed
= !value
.getExcludeFromIndexes();
443 } else if (value
.getArrayValue().getValuesCount() > 0) {
445 ArrayList
<Object
> resultList
=
446 new ArrayList
<Object
>(value
.getArrayValue().getValuesCount());
447 for (Value subValue
: value
.getArrayValue().getValuesList()) {
448 if (subValue
.getValueTypeCase() == ValueTypeCase
.ARRAY_VALUE
) {
449 throw new IllegalArgumentException("Invalid Entity PB: list within a list.");
451 result
= getValue(subValue
);
452 if (!subValue
.getExcludeFromIndexes()) {
454 if (result
instanceof EmbeddedEntity
) {
455 isOrContainsIndexedEntityValue
= true;
458 resultList
.add(result
);
462 indexed
= !value
.getExcludeFromIndexes();
463 result
= getValue(value
);
464 if (indexed
&& result
instanceof EmbeddedEntity
) {
465 isOrContainsIndexedEntityValue
= true;
469 if (isOrContainsIndexedEntityValue
) {
470 result
= new WrappedValueImpl(result
, true, true);
471 } else if (!indexed
) {
472 result
= new UnindexedValue(result
);
475 map
.put(name
, result
);
478 private static Object
getValue(Value value
) {
479 for (Type
<?
> type
: typeMap
.values()) {
480 if (type
.isType(value
)) {
481 return type
.getValue(value
);
487 private static Meaning
getV3MeaningOf(ValueOrBuilder value
) {
488 return Meaning
.valueOf(value
.getMeaning());
491 private static AppIdNamespace
toAppIdNamespace(PartitionIdOrBuilder partitionId
) {
492 if (partitionId
.getProjectId().equals(DatastoreApiHelper
.getCurrentProjectId())) {
493 return new AppIdNamespace(DatastoreApiHelper
.getCurrentAppId(), partitionId
.getNamespaceId());
495 Map
<String
, String
> additionalProjectIdToAppIdMap
= getAdditionalProjectIdToAppIdMap();
496 if (additionalProjectIdToAppIdMap
.containsKey(partitionId
.getProjectId())) {
497 return new AppIdNamespace(additionalProjectIdToAppIdMap
.get(partitionId
.getProjectId()),
498 partitionId
.getNamespaceId());
500 throw new IllegalStateException(String
.format(
501 "Could not determine app id corresponding to project id \"%s\". Please add the app id "
503 partitionId
.getProjectId(), RemoteCloudDatastoreV1Proxy
.ADDITIONAL_APP_IDS_VAR
));
507 @SuppressWarnings("unchecked")
508 private static Map
<String
, String
> getAdditionalProjectIdToAppIdMap() {
509 if (ApiProxy
.getCurrentEnvironment() != null) {
510 Object attribute
= ApiProxy
.getCurrentEnvironment().getAttributes()
511 .get(ADDITIONAL_APP_IDS_MAP_ATTRIBUTE_KEY
);
512 if (attribute
!= null) {
513 return (Map
<String
, String
>) attribute
;
516 return Collections
.emptyMap();
519 private static PartitionId
.Builder
toV1PartitionId(AppIdNamespace appNs
) {
520 PartitionId
.Builder builder
= PartitionId
.newBuilder();
521 builder
.setProjectId(DatastoreApiHelper
.toProjectId(appNs
.getAppId()));
522 if (!appNs
.getNamespace().isEmpty()) {
523 builder
.setNamespaceId(appNs
.getNamespace());
528 static com
.google
.datastore
.v1beta3
.Key
.Builder
toV1Key(Key key
) {
529 com
.google
.datastore
.v1beta3
.Key
.Builder builder
=
530 com
.google
.datastore
.v1beta3
.Key
.newBuilder();
531 builder
.setPartitionId(toV1PartitionId(key
.getAppIdNamespace()));
532 List
<PathElement
> pathElementList
= new ArrayList
<>();
534 PathElement
.Builder pathElement
= PathElement
.newBuilder();
535 pathElement
.setKind(key
.getKind());
536 if (key
.getName() != null) {
537 pathElement
.setName(key
.getName());
538 } else if (key
.getId() != Key
.NOT_ASSIGNED
) {
539 pathElement
.setId(key
.getId());
541 pathElementList
.add(pathElement
.build());
542 key
= key
.getParent();
543 } while (key
!= null);
544 builder
.addAllPath(Lists
.reverse(pathElementList
));
548 static Key
toKey(KeyOrBuilder proto
) {
549 if (proto
.getPathCount() == 0) {
550 throw new IllegalArgumentException("Invalid Key PB: no elements.");
552 AppIdNamespace appIdNamespace
= toAppIdNamespace(proto
.getPartitionId());
554 for (PathElement e
: proto
.getPathList()) {
555 String kind
= e
.getKind();
556 key
= new Key(kind
, key
, e
.getId(),
557 e
.getIdTypeCase() == IdTypeCase
.NAME ? e
.getName() : null, appIdNamespace
);
562 static Entity
toEntity(EntityOrBuilder entityV1
) {
563 Entity entity
= new Entity(DataTypeTranslator
.toKey(entityV1
.getKey()));
564 DataTypeTranslator
.extractPropertiesFromPb(entityV1
, false,
565 entity
.getPropertyMap());
569 static Entity
toEntity(EntityOrBuilder entityV1
, Collection
<Projection
> projections
) {
570 Entity entity
= new Entity(DataTypeTranslator
.toKey(entityV1
.getKey()));
572 Map
<String
, Object
> values
= Maps
.newHashMap();
573 DataTypeTranslator
.extractPropertiesFromPb(entityV1
, true, values
);
574 for (Projection projection
: projections
) {
575 entity
.setProperty(projection
.getName(), projection
.getValue(values
));
580 static com
.google
.datastore
.v1beta3
.Entity
.Builder
toV1Entity(Entity entity
) {
581 com
.google
.datastore
.v1beta3
.Entity
.Builder entityV1
=
582 com
.google
.datastore
.v1beta3
.Entity
.newBuilder();
583 entityV1
.setKey(toV1Key(entity
.getKey()));
584 addPropertiesToPb(entity
.getPropertyMap(), entityV1
);
589 * Returns the value for the property as its comparable representation type.
591 * @param property a not {@code null} property
592 * @return {@code null} if no value was set for {@code property}
594 @SuppressWarnings("unchecked")
595 public static Comparable
<Object
> getComparablePropertyValue(Property property
) {
596 return (Comparable
<Object
>) RAW_VALUE_TYPE
.asComparable(new RawValue(property
.getValue()));
600 * Converts the given {@link Object} into a supported value then returns it as
601 * a comparable object so it can be compared to other data types.
603 * @param value any Object that can be converted into a supported DataType
604 * @return {@code null} if value is null
605 * @throws UnsupportedOperationException if value is not supported
607 @SuppressWarnings("unchecked")
608 static Comparable
<Object
> getComparablePropertyValue(Object value
) {
609 return value
== null ?
null
610 : (Comparable
<Object
>) getType(value
.getClass()).asComparable(value
);
614 * Get the rank of the given datastore type relative to other datastore
615 * types. Note that datastore types do not necessarily have unique ranks.
617 @SuppressWarnings({"unchecked", "rawtypes"})
618 public static int getTypeRank(Class
<?
extends Comparable
> datastoreType
) {
619 return comparableTypeMap
.get(datastoreType
);
623 * Gets the {@link Type} that knows how to translate objects of
624 * type {@code clazz} into protocol buffers that the data store can
626 * @throws UnsupportedOperationException if clazz is not supported
628 @SuppressWarnings("unchecked")
629 private static <T
> Type
<T
> getType(Class
<T
> clazz
) {
630 if (typeMap
.containsKey(clazz
)) {
631 return (Type
<T
>) typeMap
.get(clazz
);
633 throw new UnsupportedOperationException("Unsupported data type: " + clazz
.getName());
638 * {@code Type} is an abstract class that knows how to convert Java
639 * objects of one or more types into datastore representations.
641 * @param <T> The canonical Java class for this type.
643 abstract static class Type
<T
> {
645 * @return {@code true} if the given meaning and property value matches this {@link Type}.
647 public final boolean isType(Meaning meaning
, PropertyValue propertyValue
) {
648 return meaning
== getV3Meaning() && hasValue(propertyValue
);
652 * @return {@code true} if the given value matches this {@link Type}.
654 public boolean isType(Value propertyValue
) {
655 return getV3MeaningOf(propertyValue
) == getV3Meaning() && hasValue(propertyValue
);
659 * Returns the {@link Comparable} for the given value, or
660 * {@code null} if values of this type are not comparable.
662 public abstract Comparable
<?
> asComparable(Object value
);
665 * Sets the value of {@code propertyValue} to {@code value}.
667 public abstract void toV3Value(Object value
, PropertyValue propertyValue
);
670 * @returns Whether the value is indexable
672 public abstract boolean canBeIndexed();
675 * Returns a new Cloud Datastore v1 Value for the given parameters.
677 * @param value the Java native value to convert
678 * @param indexed the desired indexing, ignored for types that are not indexable
679 * @return the Cloud Datastore v1 representation of the given value and desired indexing
681 public abstract Value
.Builder
toV1Value(
682 Object value
, boolean indexed
, boolean forceIndexedEmbeddedEntity
);
685 * Returns the value of {@code propertyValue} as its canonical Java type.
687 * Use {@link #isType} first to determine if the property has a value of the given type.
689 * @param propertyValue a not {@code null} value representing this {@code Type}
690 * @return the canonical Java representation of {@code propertyValue}.
692 public abstract T
getValue(PropertyValue propertyValue
);
695 * @see Type#getValue(PropertyValue)
697 public abstract T
getValue(Value value
);
700 * @return {@code true} if a value of this {@code Type} is set on the given propertyValue.
702 public abstract boolean hasValue(PropertyValue propertyValue
);
705 * @return {@code true} if a value of this {@code Type} is set on the given propertyValue.
707 public abstract boolean hasValue(Value propertyValue
);
710 * @return the {@link Meaning} for this {@link Type}
712 protected Meaning
getV3Meaning() {
713 return Meaning
.NO_MEANING
;
718 * A base class with common functions for types that have the same datastore representation.
720 * @param <S> the datastore type
721 * @param <T> the canonical Java class for this type
723 private abstract static class BaseVariantType
<S
, T
> extends Type
<T
> {
725 * @return the datastore representation of the given value
727 protected abstract S
toDatastoreValue(Object value
);
729 * @return the native representation of the given value
731 protected abstract T
fromDatastoreValue(S datastoreValue
);
735 * Base class for types that store strings in the datastore.
737 * @param <T> the canonical Java class for this type
739 private abstract static class BaseStringType
<T
> extends BaseVariantType
<String
, T
> {
741 public void toV3Value(Object value
, PropertyValue propertyValue
) {
742 propertyValue
.setStringValue(toDatastoreValue(value
));
746 public boolean canBeIndexed() {
751 public Value
.Builder
toV1Value(
752 Object value
, boolean indexed
, boolean forceIndexedEmbeddedEntity
) {
753 Value
.Builder builder
= Value
.newBuilder();
754 builder
.setStringValue(toDatastoreValue(value
));
755 builder
.setExcludeFromIndexes(!indexed
);
756 builder
.setMeaning(getV3Meaning().getValue());
761 public final T
getValue(PropertyValue propertyValue
) {
762 return fromDatastoreValue(propertyValue
.getStringValue());
766 public T
getValue(Value propertyValue
) {
767 return fromDatastoreValue(propertyValue
.getStringValue());
771 public final boolean hasValue(PropertyValue propertyValue
) {
772 return propertyValue
.hasStringValue();
776 public boolean hasValue(Value propertyValue
) {
777 return propertyValue
.getValueTypeCase() == ValueTypeCase
.STRING_VALUE
;
781 public ComparableByteArray
asComparable(Object value
) {
782 return new ComparableByteArray(ProtocolSupport
.toBytesUtf8(toDatastoreValue(value
)));
787 * Base class for types that store bytes in the datastore.
789 * @param <T> the canonical Java class for this type
791 private abstract static class BaseBlobType
<T
> extends BaseVariantType
<byte[], T
> {
792 protected abstract boolean isIndexable();
795 public final boolean hasValue(PropertyValue propertyValue
) {
796 return propertyValue
.hasStringValue();
800 public final void toV3Value(Object value
, PropertyValue propertyValue
) {
801 propertyValue
.setStringValueAsBytes(toDatastoreValue(value
));
805 public boolean canBeIndexed() {
806 return isIndexable();
810 public Value
.Builder
toV1Value(
811 Object value
, boolean indexed
, boolean forceIndexedEmbeddedEntity
) {
812 Value
.Builder builder
= Value
.newBuilder();
813 builder
.setBlobValue(ByteString
.copyFrom(toDatastoreValue(value
)));
814 builder
.setExcludeFromIndexes(!indexed
|| !isIndexable());
819 public final T
getValue(PropertyValue propertyValue
) {
820 return fromDatastoreValue(propertyValue
.getStringValueAsBytes());
824 public final ComparableByteArray
asComparable(Object value
) {
825 return isIndexable() ?
new ComparableByteArray(toDatastoreValue(value
)) : null;
830 * Base class for types that store predefined entities in Cloud Datastore v1.
832 * @param <T> the canonical Java class for this type
834 private abstract static class BasePredefinedEntityType
<T
> extends Type
<T
> {
836 * @return the predefined entity meaning to use in Cloud Datastore v1
838 protected abstract int getV1Meaning();
841 * @return the Cloud Datastore v1 Entity representation for the given value
843 protected abstract com
.google
.datastore
.v1beta3
.Entity
getEntity(Object value
);
846 public final boolean isType(Value propertyValue
) {
847 return propertyValue
.getMeaning() == getV1Meaning() && hasValue(propertyValue
);
851 public final boolean hasValue(Value propertyValue
) {
852 return propertyValue
.getValueTypeCase() == ValueTypeCase
.ENTITY_VALUE
;
856 public final Value
.Builder
toV1Value(
857 Object value
, boolean indexed
, boolean forceIndexedEmbeddedEntity
) {
858 Value
.Builder builder
= Value
.newBuilder();
859 builder
.setEntityValue(getEntity(value
));
860 builder
.setExcludeFromIndexes(!indexed
);
861 builder
.setMeaning(getV1Meaning());
867 * Returns the Cloud Datastore v1 value representation for the given value, unindexed.
869 private static Value
makeUnindexedValue(String value
) {
870 return Value
.newBuilder().setStringValue(value
).setExcludeFromIndexes(true).build();
874 * Base class for types that int64 values in the datastore.
876 * @param <T> the canonical Java class for this type
878 private abstract static class BaseInt64Type
<T
> extends BaseVariantType
<Long
, T
> {
880 public final void toV3Value(Object value
, PropertyValue propertyValue
) {
881 propertyValue
.setInt64Value(toDatastoreValue(value
));
885 public boolean canBeIndexed() {
890 public Value
.Builder
toV1Value(
891 Object value
, boolean indexed
, boolean forceIndexedEmbeddedEntity
) {
892 Value
.Builder builder
= Value
.newBuilder();
893 builder
.setIntegerValue(toDatastoreValue(value
));
894 builder
.setExcludeFromIndexes(!indexed
);
895 builder
.setMeaning(getV3Meaning().getValue());
900 public T
getValue(PropertyValue propertyValue
) {
901 return fromDatastoreValue(propertyValue
.getInt64Value());
905 public T
getValue(Value propertyValue
) {
906 return fromDatastoreValue(propertyValue
.getIntegerValue());
910 public boolean hasValue(PropertyValue propertyValue
) {
911 return propertyValue
.hasInt64Value();
915 public boolean hasValue(Value propertyValue
) {
916 return propertyValue
.getValueTypeCase() == ValueTypeCase
.INTEGER_VALUE
;
920 public Long
asComparable(Object value
) {
921 return toDatastoreValue(value
);
926 * The type for projected index values.
928 private static final class RawValueType
extends Type
<RawValue
> {
930 public Meaning
getV3Meaning() {
931 return Meaning
.INDEX_VALUE
;
935 public boolean hasValue(PropertyValue propertyValue
) {
940 public boolean hasValue(Value propertyValue
) {
945 public void toV3Value(Object value
, PropertyValue propertyValue
) {
946 throw new UnsupportedOperationException();
950 public boolean canBeIndexed() {
955 public Value
.Builder
toV1Value(
956 Object value
, boolean indexed
, boolean forceIndexedEmbeddedEntity
) {
957 throw new UnsupportedOperationException();
961 public RawValue
getValue(PropertyValue propertyValue
) {
962 return new RawValue(propertyValue
);
966 public RawValue
getValue(Value propertyValue
) {
967 return new RawValue(propertyValue
);
970 @SuppressWarnings("unchecked")
972 public Comparable
<?
> asComparable(Object value
) {
973 value
= ((RawValue
) value
).getValue();
974 if (value
instanceof byte[]) {
975 return new ComparableByteArray((byte[]) value
);
977 return (Comparable
<?
>) value
;
982 * The raw String type.
984 private static final class StringType
extends BaseStringType
<String
> {
986 protected String
toDatastoreValue(Object value
) {
987 return value
.toString();
991 protected String
fromDatastoreValue(String datastoreValue
) {
992 return datastoreValue
;
997 * The raw int64 type.
999 private static final class Int64Type
extends BaseInt64Type
<Long
> {
1001 protected Long
toDatastoreValue(Object value
) {
1002 return ((Number
) value
).longValue();
1006 protected Long
fromDatastoreValue(Long datastoreValue
) {
1007 return datastoreValue
;
1012 * The raw double type.
1014 private static final class DoubleType
extends Type
<Double
> {
1016 public void toV3Value(Object value
, PropertyValue propertyValue
) {
1017 propertyValue
.setDoubleValue(((Number
) value
).doubleValue());
1021 public boolean canBeIndexed() {
1026 public Value
.Builder
toV1Value(
1027 Object value
, boolean indexed
, boolean forceIndexedEmbeddedEntity
) {
1028 Value
.Builder builder
= Value
.newBuilder();
1029 builder
.setDoubleValue(((Number
) value
).doubleValue());
1030 builder
.setExcludeFromIndexes(!indexed
);
1035 public Double
getValue(PropertyValue propertyValue
) {
1036 return propertyValue
.getDoubleValue();
1040 public Double
getValue(Value propertyValue
) {
1041 return propertyValue
.getDoubleValue();
1045 public boolean hasValue(PropertyValue propertyValue
) {
1046 return propertyValue
.hasDoubleValue();
1050 public boolean hasValue(Value propertyValue
) {
1051 return propertyValue
.getValueTypeCase() == ValueTypeCase
.DOUBLE_VALUE
;
1055 public Double
asComparable(Object value
) {
1056 return ((Number
) value
).doubleValue();
1061 * The raw boolean type.
1063 private static final class BoolType
extends Type
<Boolean
> {
1065 public void toV3Value(Object value
, PropertyValue propertyValue
) {
1066 propertyValue
.setBooleanValue((Boolean
) value
);
1070 public boolean canBeIndexed() {
1075 public Value
.Builder
toV1Value(
1076 Object value
, boolean indexed
, boolean forceIndexedEmbeddedEntity
) {
1077 Value
.Builder builder
= Value
.newBuilder();
1078 builder
.setBooleanValue((Boolean
) value
);
1079 builder
.setExcludeFromIndexes(!indexed
);
1084 public Boolean
getValue(PropertyValue propertyValue
) {
1085 return propertyValue
.isBooleanValue();
1089 public Boolean
getValue(Value propertyValue
) {
1090 return propertyValue
.getBooleanValue();
1094 public boolean hasValue(PropertyValue propertyValue
) {
1095 return propertyValue
.hasBooleanValue();
1099 public boolean hasValue(Value propertyValue
) {
1100 return propertyValue
.getValueTypeCase() == ValueTypeCase
.BOOLEAN_VALUE
;
1104 public Boolean
asComparable(Object value
) {
1105 return (Boolean
) value
;
1113 * <p>Stored as an entity with a special meaning in v1.
1115 private static final class UserType
extends BasePredefinedEntityType
<User
> {
1116 public static final int MEANING_PREDEFINED_ENTITY_USER
= 20;
1117 public static final String PROPERTY_NAME_EMAIL
= "email";
1118 public static final String PROPERTY_NAME_AUTH_DOMAIN
= "auth_domain";
1119 public static final String PROPERTY_NAME_USER_ID
= "user_id";
1122 public int getV1Meaning() {
1123 return MEANING_PREDEFINED_ENTITY_USER
;
1127 public com
.google
.datastore
.v1beta3
.Entity
getEntity(Object value
) {
1128 User user
= (User
) value
;
1129 com
.google
.datastore
.v1beta3
.Entity
.Builder builder
=
1130 com
.google
.datastore
.v1beta3
.Entity
.newBuilder();
1131 builder
.getMutableProperties().put(PROPERTY_NAME_EMAIL
, makeUnindexedValue(user
.getEmail()));
1132 builder
.getMutableProperties().put(PROPERTY_NAME_AUTH_DOMAIN
,
1133 makeUnindexedValue(user
.getAuthDomain()));
1134 if (user
.getUserId() != null) {
1135 builder
.getMutableProperties().put(PROPERTY_NAME_USER_ID
,
1136 makeUnindexedValue(user
.getUserId()));
1138 return builder
.build();
1142 public void toV3Value(Object value
, PropertyValue propertyValue
) {
1143 User user
= (User
) value
;
1144 UserValue userValue
= new UserValue();
1145 userValue
.setEmail(user
.getEmail());
1146 userValue
.setAuthDomain(user
.getAuthDomain());
1147 if (user
.getUserId() != null) {
1148 userValue
.setObfuscatedGaiaid(user
.getUserId());
1150 userValue
.setGaiaid(0);
1151 propertyValue
.setUserValue(userValue
);
1155 public boolean canBeIndexed() {
1160 public User
getValue(PropertyValue propertyValue
) {
1161 UserValue userValue
= propertyValue
.getUserValue();
1162 String userId
= userValue
.hasObfuscatedGaiaid() ? userValue
.getObfuscatedGaiaid() : null;
1163 return new User(userValue
.getEmail(), userValue
.getAuthDomain(), userId
);
1167 public User
getValue(Value propertyValue
) {
1169 String authDomain
= "";
1170 String userId
= null;
1171 for (Map
.Entry
<String
, Value
> prop
1172 : propertyValue
.getEntityValueOrBuilder().getProperties().entrySet()) {
1173 if (prop
.getKey().equals(PROPERTY_NAME_EMAIL
)) {
1174 email
= prop
.getValue().getStringValue();
1175 } else if (prop
.getKey().equals(PROPERTY_NAME_AUTH_DOMAIN
)) {
1176 authDomain
= prop
.getValue().getStringValue();
1177 } else if (prop
.getKey().equals(PROPERTY_NAME_USER_ID
)) {
1178 userId
= prop
.getValue().getStringValue();
1181 return new User(email
, authDomain
, userId
);
1185 public boolean hasValue(PropertyValue propertyValue
) {
1186 return propertyValue
.hasUserValue();
1190 public final Comparable
<User
> asComparable(Object value
) {
1191 return (User
) value
;
1198 * <p>Stored as a GeoPoint value with no meaning in Cloud Datastore v1.
1200 private static class GeoPtType
extends Type
<GeoPt
> {
1202 public boolean isType(Value propertyValue
) {
1203 return propertyValue
.getValueTypeCase() == ValueTypeCase
.GEO_POINT_VALUE
1204 && propertyValue
.getMeaning() == 0;
1208 public void toV3Value(Object value
, PropertyValue propertyValue
) {
1209 GeoPt geoPt
= (GeoPt
) value
;
1210 PropertyValue
.PointValue pv
= new PropertyValue
.PointValue()
1211 .setX(geoPt
.getLatitude())
1212 .setY(geoPt
.getLongitude());
1213 propertyValue
.setPointValue(pv
);
1217 public boolean canBeIndexed() {
1222 public final Value
.Builder
toV1Value(
1223 Object value
, boolean indexed
, boolean forceIndexedEmbeddedEntity
) {
1224 GeoPt geoPt
= (GeoPt
) value
;
1225 Value
.Builder builder
= Value
.newBuilder();
1226 builder
.getGeoPointValueBuilder()
1227 .setLatitude(geoPt
.getLatitude())
1228 .setLongitude(geoPt
.getLongitude());
1229 builder
.setExcludeFromIndexes(!indexed
);
1234 public GeoPt
getValue(PropertyValue propertyValue
) {
1235 PropertyValue
.PointValue pv
= propertyValue
.getPointValue();
1236 return new GeoPt((float) pv
.getX(), (float) pv
.getY());
1240 public GeoPt
getValue(Value propertyValue
) {
1242 (float) propertyValue
.getGeoPointValue().getLatitude(),
1243 (float) propertyValue
.getGeoPointValue().getLongitude());
1247 public boolean hasValue(PropertyValue propertyValue
) {
1248 return propertyValue
.hasPointValue();
1252 public final boolean hasValue(Value propertyValue
) {
1253 return propertyValue
.getValueTypeCase() == ValueTypeCase
.GEO_POINT_VALUE
;
1257 public Meaning
getV3Meaning() {
1258 return Meaning
.GEORSS_POINT
;
1262 public final Comparable
<GeoPt
> asComparable(Object value
) {
1263 return (GeoPt
) value
;
1268 * The key/reference type.
1270 private static final class KeyType
extends Type
<Key
> {
1272 public void toV3Value(Object value
, PropertyValue propertyValue
) {
1273 Reference keyRef
= KeyTranslator
.convertToPb((Key
) value
);
1274 propertyValue
.setReferenceValue(toReferenceValue(keyRef
));
1278 public boolean canBeIndexed() {
1283 public Value
.Builder
toV1Value(
1284 Object value
, boolean indexed
, boolean forceIndexedEmbeddedEntity
) {
1285 Value
.Builder builder
= Value
.newBuilder();
1286 builder
.setKeyValue(toV1Key((Key
) value
));
1287 builder
.setExcludeFromIndexes(!indexed
);
1292 public Key
getValue(PropertyValue propertyValue
) {
1293 return KeyTranslator
.createFromPb(toReference(propertyValue
.getReferenceValue()));
1297 public Key
getValue(Value propertyValue
) {
1298 return toKey(propertyValue
.getKeyValue());
1302 public boolean hasValue(PropertyValue propertyValue
) {
1303 return propertyValue
.hasReferenceValue();
1307 public boolean hasValue(Value propertyValue
) {
1308 return propertyValue
.getValueTypeCase() == ValueTypeCase
.KEY_VALUE
;
1312 public Key
asComparable(Object value
) {
1316 private static ReferenceValue
toReferenceValue(Reference keyRef
) {
1317 ReferenceValue refValue
= new ReferenceValue();
1318 refValue
.setApp(keyRef
.getApp());
1319 if (keyRef
.hasNameSpace()) {
1320 refValue
.setNameSpace(keyRef
.getNameSpace());
1322 Path path
= keyRef
.getPath();
1323 for (Element element
: path
.elements()) {
1324 ReferenceValuePathElement newElement
= new ReferenceValuePathElement();
1325 newElement
.setType(element
.getType());
1326 if (element
.hasName()) {
1327 newElement
.setName(element
.getName());
1329 if (element
.hasId()) {
1330 newElement
.setId(element
.getId());
1332 refValue
.addPathElement(newElement
);
1338 private static Reference
toReference(ReferenceValue refValue
) {
1339 Reference reference
= new Reference();
1340 reference
.setApp(refValue
.getApp());
1341 if (refValue
.hasNameSpace()) {
1342 reference
.setNameSpace(refValue
.getNameSpace());
1344 Path path
= new Path();
1345 for (ReferenceValuePathElement element
: refValue
.pathElements()) {
1346 Element newElement
= new Element();
1347 newElement
.setType(element
.getType());
1348 if (element
.hasName()) {
1349 newElement
.setName(element
.getName());
1351 if (element
.hasId()) {
1352 newElement
.setId(element
.getId());
1354 path
.addElement(newElement
);
1356 reference
.setPath(path
);
1362 * The non-indexable blob type.
1364 private static class BlobType
extends BaseBlobType
<Blob
> {
1366 public Meaning
getV3Meaning() {
1367 return Meaning
.BLOB
;
1371 public boolean isType(Value propertyValue
) {
1372 return getV3MeaningOf(propertyValue
) == Meaning
.NO_MEANING
1373 && propertyValue
.getExcludeFromIndexes()
1374 && hasValue(propertyValue
);
1378 public boolean hasValue(Value propertyValue
) {
1379 return propertyValue
.getValueTypeCase() == ValueTypeCase
.BLOB_VALUE
;
1383 public Blob
getValue(Value propertyValue
) {
1384 return fromDatastoreValue(propertyValue
.getBlobValue().toByteArray());
1388 protected Blob
fromDatastoreValue(byte[] datastoreValue
) {
1389 return new Blob(datastoreValue
);
1393 protected byte[] toDatastoreValue(Object value
) {
1394 return ((Blob
) value
).getBytes();
1398 public boolean isIndexable() {
1404 * The indexable blob type.
1406 private static class ShortBlobType
extends BaseBlobType
<ShortBlob
> {
1408 public Meaning
getV3Meaning() {
1409 return Meaning
.BYTESTRING
;
1413 public boolean isType(Value propertyValue
) {
1414 if (!hasValue(propertyValue
)) {
1418 if (propertyValue
.getExcludeFromIndexes()) {
1419 return getV3MeaningOf(propertyValue
) == getV3Meaning();
1421 return getV3MeaningOf(propertyValue
) == Meaning
.NO_MEANING
;
1426 public Value
.Builder
toV1Value(
1427 Object value
, boolean indexed
, boolean forceIndexedEmbeddedEntity
) {
1428 Value
.Builder builder
= super.toV1Value(value
, indexed
, forceIndexedEmbeddedEntity
);
1430 builder
.setMeaning(getV3Meaning().getValue());
1436 public boolean hasValue(Value propertyValue
) {
1437 return propertyValue
.getValueTypeCase() == ValueTypeCase
.BLOB_VALUE
1438 || (getV3MeaningOf(propertyValue
) == Meaning
.INDEX_VALUE
1439 && propertyValue
.getValueTypeCase() == ValueTypeCase
.STRING_VALUE
);
1443 public ShortBlob
getValue(Value propertyValue
) {
1444 if (getV3MeaningOf(propertyValue
) == Meaning
.INDEX_VALUE
1445 && propertyValue
.getValueTypeCase() == ValueTypeCase
.STRING_VALUE
) {
1446 return fromDatastoreValue(propertyValue
.getStringValueBytes().toByteArray());
1448 return fromDatastoreValue(propertyValue
.getBlobValue().toByteArray());
1453 protected byte[] toDatastoreValue(Object value
) {
1454 return ((ShortBlob
) value
).getBytes();
1458 protected ShortBlob
fromDatastoreValue(byte[] datastoreValue
) {
1459 return new ShortBlob(datastoreValue
);
1463 public boolean isIndexable() {
1471 * Stored as a partially serialized EntityProto in V3.
1473 private static final class EmbeddedEntityType
extends Type
<EmbeddedEntity
> {
1475 public Meaning
getV3Meaning() {
1476 return Meaning
.ENTITY_PROTO
;
1480 public boolean isType(Value propertyValue
) {
1481 return getV3MeaningOf(propertyValue
) == Meaning
.NO_MEANING
1482 && hasValue(propertyValue
);
1486 public boolean hasValue(PropertyValue propertyValue
) {
1487 return propertyValue
.hasStringValue();
1491 public boolean hasValue(Value propertyValue
) {
1492 return propertyValue
.getValueTypeCase() == ValueTypeCase
.ENTITY_VALUE
;
1496 public EmbeddedEntity
getValue(PropertyValue propertyValue
) {
1497 EntityProto proto
= new EntityProto();
1498 proto
.mergeFrom(propertyValue
.getStringValueAsBytes());
1499 EmbeddedEntity result
= new EmbeddedEntity();
1500 if (proto
.hasKey() && !proto
.getKey().getApp().isEmpty()) {
1501 result
.setKey(KeyTranslator
.createFromPb(proto
.getKey()));
1503 extractPropertiesFromPb(proto
, result
.getPropertyMap());
1508 public EmbeddedEntity
getValue(Value propertyValue
) {
1509 EmbeddedEntity result
= new EmbeddedEntity();
1510 com
.google
.datastore
.v1beta3
.Entity proto
= propertyValue
.getEntityValue();
1511 if (proto
.hasKey()) {
1512 result
.setKey(toKey(proto
.getKey()));
1514 extractPropertiesFromPb(proto
, false, result
.getPropertyMap());
1519 public void toV3Value(Object value
, PropertyValue propertyValue
) {
1520 EmbeddedEntity structProp
= (EmbeddedEntity
) value
;
1521 EntityProto proto
= new EntityProto();
1522 if (structProp
.getKey() != null) {
1523 proto
.setKey(KeyTranslator
.convertToPb(structProp
.getKey()));
1525 addPropertiesToPb(structProp
.getPropertyMap(), proto
);
1526 propertyValue
.setStringValueAsBytes(proto
.toByteArray());
1530 public boolean canBeIndexed() {
1535 public Value
.Builder
toV1Value(
1536 Object value
, boolean indexed
, boolean forceIndexedEmbeddedEntity
) {
1537 EmbeddedEntity structProp
= (EmbeddedEntity
) value
;
1538 Value
.Builder builder
= Value
.newBuilder();
1539 com
.google
.datastore
.v1beta3
.Entity
.Builder proto
= builder
.getEntityValueBuilder();
1540 if (structProp
.getKey() != null) {
1541 proto
.setKey(toV1Key(structProp
.getKey()));
1543 addPropertiesToPb(structProp
.getPropertyMap(), proto
);
1544 builder
.setExcludeFromIndexes(!indexed
|| !forceIndexedEmbeddedEntity
);
1549 public Comparable
<?
> asComparable(Object value
) {
1555 * The non-indexable {@link Text} type.
1557 private static final class TextType
extends BaseStringType
<Text
> {
1559 public Meaning
getV3Meaning() {
1560 return Meaning
.TEXT
;
1564 public void toV3Value(Object value
, PropertyValue propertyValue
) {
1565 super.toV3Value(value
, propertyValue
);
1569 public boolean canBeIndexed() {
1574 public Value
.Builder
toV1Value(
1575 Object value
, boolean indexed
, boolean forceIndexedEmbeddedEntity
) {
1576 return super.toV1Value(value
, false, false);
1580 protected Text
fromDatastoreValue(String datastoreString
) {
1581 return new Text(datastoreString
);
1585 protected String
toDatastoreValue(Object value
) {
1586 return ((Text
) value
).getValue();
1590 public ComparableByteArray
asComparable(Object value
) {
1596 * The {@link BlobKey} type. Blob keys are just strings with a special meaning.
1598 private static final class BlobKeyType
extends BaseStringType
<BlobKey
> {
1600 public Meaning
getV3Meaning() {
1601 return Meaning
.BLOBKEY
;
1605 public Value
.Builder
toV1Value(
1606 Object value
, boolean indexed
, boolean forceIndexedEmbeddedEntity
) {
1607 Value
.Builder builder
= Value
.newBuilder();
1608 builder
.setStringValue(toDatastoreValue(value
));
1609 builder
.setMeaning(Meaning
.BLOBKEY
.getValue());
1610 builder
.setExcludeFromIndexes(!indexed
);
1615 public BlobKey
getValue(Value propertyValue
) {
1616 return fromDatastoreValue(propertyValue
.getStringValue());
1620 protected String
toDatastoreValue(Object value
) {
1621 return ((BlobKey
) value
).getKeyString();
1625 protected BlobKey
fromDatastoreValue(String datastoreString
) {
1626 return new BlobKey(datastoreString
);
1633 * In V3 dates are just int64s with a special meaning.
1635 private static final class DateType
extends BaseInt64Type
<Date
> {
1637 public Meaning
getV3Meaning() {
1638 return Meaning
.GD_WHEN
;
1642 public boolean isType(Value propertyValue
) {
1643 return propertyValue
.getMeaning() == 0 && hasValue(propertyValue
);
1647 public boolean hasValue(Value propertyValue
) {
1648 return propertyValue
.getValueTypeCase() == ValueTypeCase
.TIMESTAMP_VALUE
1649 || (getV3MeaningOf(propertyValue
) == Meaning
.INDEX_VALUE
1650 && propertyValue
.getValueTypeCase() == ValueTypeCase
.INTEGER_VALUE
);
1654 public Value
.Builder
toV1Value(
1655 Object value
, boolean indexed
, boolean forceIndexedEmbeddedEntity
) {
1656 Value
.Builder builder
= DatastoreHelper
.makeValue((Date
) value
);
1657 builder
.setExcludeFromIndexes(!indexed
);
1662 public Date
getValue(Value propertyValue
) {
1663 if (getV3MeaningOf(propertyValue
) == Meaning
.INDEX_VALUE
1664 && propertyValue
.getValueTypeCase() == ValueTypeCase
.INTEGER_VALUE
) {
1665 return fromDatastoreValue(propertyValue
.getIntegerValue());
1667 long datastoreValue
= DatastoreHelper
.getTimestamp(propertyValue
);
1668 return fromDatastoreValue(datastoreValue
);
1673 protected Long
toDatastoreValue(Object value
) {
1674 return ((Date
) value
).getTime() * 1000L;
1678 protected Date
fromDatastoreValue(Long datastoreValue
) {
1679 return new Date(datastoreValue
/ 1000L);
1684 * Internally a link is just a string with a special meaning.
1686 private static final class LinkType
extends BaseStringType
<Link
> {
1688 public Meaning
getV3Meaning() {
1689 return Meaning
.ATOM_LINK
;
1693 protected String
toDatastoreValue(Object value
) {
1694 return ((Link
) value
).getValue();
1698 protected Link
fromDatastoreValue(String datastoreValue
) {
1699 return new Link(datastoreValue
);
1704 * Internally a category is just a string with a special meaning.
1706 private static final class CategoryType
extends BaseStringType
<Category
> {
1708 public Meaning
getV3Meaning() {
1709 return Meaning
.ATOM_CATEGORY
;
1713 protected String
toDatastoreValue(Object value
) {
1714 return ((Category
) value
).getCategory();
1718 protected Category
fromDatastoreValue(String datastoreString
) {
1719 return new Category(datastoreString
);
1724 * Internally a rating is just an int64 with a special meaning.
1726 private static final class RatingType
extends BaseInt64Type
<Rating
> {
1728 public Meaning
getV3Meaning() {
1729 return Meaning
.GD_RATING
;
1733 protected Long
toDatastoreValue(Object value
) {
1734 return (long) ((Rating
) value
).getRating();
1738 protected Rating
fromDatastoreValue(Long datastoreLong
) {
1739 return new Rating(datastoreLong
.intValue());
1744 * Internally an email is just a string with a special meaning.
1746 private static final class EmailType
extends BaseStringType
<Email
> {
1748 public Meaning
getV3Meaning() {
1749 return Meaning
.GD_EMAIL
;
1753 protected String
toDatastoreValue(Object value
) {
1754 return ((Email
) value
).getEmail();
1758 protected Email
fromDatastoreValue(String datastoreString
) {
1759 return new Email(datastoreString
);
1764 * Internally a postal address is just a string with a special meaning.
1766 private static final class PostalAddressType
extends BaseStringType
<PostalAddress
> {
1768 public Meaning
getV3Meaning() {
1769 return Meaning
.GD_POSTALADDRESS
;
1773 protected String
toDatastoreValue(Object value
) {
1774 return ((PostalAddress
) value
).getAddress();
1778 protected PostalAddress
fromDatastoreValue(String datastoreString
) {
1779 return new PostalAddress(datastoreString
);
1784 * Internally a phone number is just a string with a special meaning.
1786 private static final class PhoneNumberType
extends BaseStringType
<PhoneNumber
> {
1788 public Meaning
getV3Meaning() {
1789 return Meaning
.GD_PHONENUMBER
;
1793 protected String
toDatastoreValue(Object value
) {
1794 return ((PhoneNumber
) value
).getNumber();
1798 protected PhoneNumber
fromDatastoreValue(String datastoreString
) {
1799 return new PhoneNumber(datastoreString
);
1804 * Internally an IM handle is just a string with a special meaning and a
1805 * well known format.
1807 private static final class IMHandleType
extends BaseStringType
<IMHandle
> {
1809 public Meaning
getV3Meaning() {
1810 return Meaning
.GD_IM
;
1814 protected String
toDatastoreValue(Object value
) {
1815 return ((IMHandle
) value
).toDatastoreString();
1819 protected IMHandle
fromDatastoreValue(String datastoreString
) {
1820 return IMHandle
.fromDatastoreString(datastoreString
);
1824 static Map
<Class
<?
>, Type
<?
>> getTypeMap() {
1829 * A wrapper for a {@code byte[]} that implements {@link Comparable}.
1830 * Comparison algorithm is the same as the prod datastore.
1832 public static final class ComparableByteArray
implements Comparable
<ComparableByteArray
> {
1833 private final byte[] bytes
;
1835 public ComparableByteArray(byte[] bytes
) {
1840 public int compareTo(ComparableByteArray other
) {
1841 byte[] otherBytes
= other
.bytes
;
1842 for (int i
= 0; i
< Math
.min(bytes
.length
, otherBytes
.length
); i
++) {
1843 int v1
= bytes
[i
] & 0xFF;
1844 int v2
= otherBytes
[i
] & 0xFF;
1849 return bytes
.length
- otherBytes
.length
;
1853 public boolean equals(Object obj
) {
1857 return Arrays
.equals(bytes
, ((ComparableByteArray
) obj
).bytes
);
1861 public int hashCode() {
1863 for (byte b
: bytes
) {
1864 result
= 31 * result
+ b
;
1870 private DataTypeTranslator() {