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
.users
.User
;
8 import com
.google
.apphosting
.datastore
.EntityV4
;
9 import com
.google
.common
.collect
.Lists
;
10 import com
.google
.common
.collect
.Maps
;
11 import com
.google
.io
.protocol
.ProtocolSupport
;
12 import com
.google
.protobuf
.ByteString
;
13 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.EntityProto
;
14 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.Path
;
15 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.Path
.Element
;
16 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.Property
;
17 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.Property
.Meaning
;
18 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.PropertyValue
;
19 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.PropertyValue
.ReferenceValue
;
20 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.PropertyValue
.ReferenceValuePathElement
;
21 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.PropertyValue
.UserValue
;
22 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.Reference
;
24 import java
.util
.ArrayList
;
25 import java
.util
.Arrays
;
26 import java
.util
.Collection
;
27 import java
.util
.Collections
;
28 import java
.util
.Date
;
29 import java
.util
.HashMap
;
30 import java
.util
.List
;
34 * {@code DataTypeTranslator} is a utility class for converting
35 * between the data store's {@code Property} protocol buffers and the
36 * user-facing classes ({@code String}, {@code User}, etc.).
39 public final class DataTypeTranslator
{
41 private static final RawValueType RAW_VALUE_TYPE
= new RawValueType();
43 * The list of supported types.
45 * Note: If you're going to modify this list, also update
46 * DataTypeUtils. We're not building {@link DataTypeUtils#getSupportedTypes}
47 * directly from this typeMap, because we want {@link DataTypeUtils} to be
48 * translatable by GWT, so that {@link Entity Entities} can be easily sent
49 * via GWT RPC. Also, if you add a type here that is not immutable you'll
50 * need to add special handling for it in {@link Entity#clone()}.
52 private static final Map
<Class
<?
>, Type
<?
>> typeMap
= Maps
.newHashMap();
54 typeMap
.put(RawValue
.class, RAW_VALUE_TYPE
);
56 typeMap
.put(Float
.class, new DoubleType());
57 typeMap
.put(Double
.class, new DoubleType());
59 typeMap
.put(Byte
.class, new Int64Type());
60 typeMap
.put(Short
.class, new Int64Type());
61 typeMap
.put(Integer
.class, new Int64Type());
62 typeMap
.put(Long
.class, new Int64Type());
63 typeMap
.put(Date
.class, new DateType());
64 typeMap
.put(Rating
.class, new RatingType());
66 typeMap
.put(String
.class, new StringType());
67 typeMap
.put(Link
.class, new LinkType());
68 typeMap
.put(ShortBlob
.class, new ShortBlobType());
69 typeMap
.put(Category
.class, new CategoryType());
70 typeMap
.put(PhoneNumber
.class, new PhoneNumberType());
71 typeMap
.put(PostalAddress
.class, new PostalAddressType());
72 typeMap
.put(Email
.class, new EmailType());
73 typeMap
.put(IMHandle
.class, new IMHandleType());
74 typeMap
.put(BlobKey
.class, new BlobKeyType());
75 typeMap
.put(Blob
.class, new BlobType());
76 typeMap
.put(Text
.class, new TextType());
77 typeMap
.put(EmbeddedEntity
.class, new EmbeddedEntityType());
79 typeMap
.put(Boolean
.class, new BoolType());
80 typeMap
.put(User
.class, new UserType());
81 typeMap
.put(Key
.class, new KeyType());
82 typeMap
.put(GeoPt
.class, new GeoPtType());
84 assert typeMap
.keySet().equals(DataTypeUtils
.getSupportedTypes())
85 : "Warning: DataTypeUtils and DataTypeTranslator do not agree "
86 + "about supported classes: " + typeMap
.keySet() + " vs. "
87 + DataTypeUtils
.getSupportedTypes();
91 * A map with the {@link Comparable} classes returned by all the instances of
92 * {@link Type#asComparable(Object)} as keys and the pb code point as the value.
93 * Used for comparing values that don't map to the same pb code point.
96 Map
<Class
<?
extends Comparable
<?
>>, Integer
> comparableTypeMap
=
97 new HashMap
<Class
<?
extends Comparable
<?
>>, Integer
>();
100 comparableTypeMap
.put(ComparableByteArray
.class, PropertyValue
.kstringValue
);
101 comparableTypeMap
.put(Long
.class, PropertyValue
.kint64Value
);
102 comparableTypeMap
.put(Double
.class, PropertyValue
.kdoubleValue
);
103 comparableTypeMap
.put(Boolean
.class, PropertyValue
.kbooleanValue
);
104 comparableTypeMap
.put(User
.class, PropertyValue
.kUserValueGroup
);
105 comparableTypeMap
.put(Key
.class, PropertyValue
.kReferenceValueGroup
);
106 comparableTypeMap
.put(GeoPt
.class, PropertyValue
.kPointValueGroup
);
110 * Add all of the properties in the specified map to an {@code EntityProto}.
111 * This involves determining the type of each property and creating the
112 * proper type-specific protocol buffer.
114 * If the property value is an {@link UnindexedValue}, or if it's a
115 * type that is never indexed, e.g. {@code Text} and {@code Blob}, it's
116 * added to {@code EntityProto.raw_property}. Otherwise it's added to
117 * {@code EntityProto.property}.
119 * @param map A not {@code null} map of all the properties which will
120 * be set on {@code proto}
121 * @param proto A not {@code null} protocol buffer
123 public static void addPropertiesToPb(Map
<String
, Object
> map
, EntityProto proto
) {
124 for (Map
.Entry
<String
, Object
> entry
: map
.entrySet()) {
125 String name
= entry
.getKey();
126 boolean indexed
= !(entry
.getValue() instanceof UnindexedValue
);
127 Object value
= PropertyContainer
.unwrapValue(entry
.getValue());
129 if (value
instanceof Collection
<?
>) {
130 Collection
<?
> values
= (Collection
<?
>) value
;
131 if (values
.isEmpty()) {
132 addPropertyToPb(name
, null, indexed
, false, proto
);
134 for (Object listValue
: values
) {
135 addPropertyToPb(name
, listValue
, indexed
, true, proto
);
139 addPropertyToPb(name
, value
, indexed
, false, proto
);
145 * Adds a property to {@code entity}.
147 * @param name the property name
148 * @param value the property value
149 * @param indexed whether this property should be indexed. This may be
150 * overriden by property types like Blob and Text that are never indexed.
151 * @param multiple whether this property has multiple values
152 * @param entity the entity to populate
154 private static void addPropertyToPb(String name
, Object value
, boolean indexed
, boolean multiple
,
155 EntityProto entity
) {
156 Property property
= new Property();
157 property
.setName(name
);
158 property
.setMultiple(multiple
);
159 PropertyValue newValue
= property
.getMutableValue();
161 Type
<?
> type
= getType(value
.getClass());
162 Meaning meaning
= type
.getV3Meaning();
163 if (meaning
!= property
.getMeaningEnum()) {
164 property
.setMeaning(meaning
);
166 indexed
&= type
.toV3Value(value
, newValue
);
169 entity
.addRawProperty(property
);
171 entity
.addProperty(property
);
175 static PropertyValue
toV3Value(Object value
) {
176 PropertyValue propertyValue
= new PropertyValue();
178 getType(value
.getClass()).toV3Value(value
, propertyValue
);
180 return propertyValue
;
184 * Copy all of the indexed properties present on {@code proto} into {@code map}.
186 public static void extractIndexedPropertiesFromPb(EntityProto proto
, Map
<String
, Object
> map
) {
187 for (Property property
: proto
.propertys()) {
188 addPropertyToMap(property
, true, map
);
193 * Copy all of the unindexed properties present on {@code proto} into {@code map}.
195 private static void extractUnindexedPropertiesFromPb(EntityProto proto
, Map
<String
, Object
> map
) {
196 for (Property property
: proto
.rawPropertys()) {
197 addPropertyToMap(property
, false, map
);
202 * Copy all of the properties present on {@code proto} into {@code map}.
204 public static void extractPropertiesFromPb(EntityProto proto
, Map
<String
, Object
> map
) {
205 extractIndexedPropertiesFromPb(proto
, map
);
206 extractUnindexedPropertiesFromPb(proto
, map
);
210 * Copy all of the implicit properties present on {@code proto} into {@code map}.
212 public static void extractImplicitPropertiesFromPb(EntityProto proto
, Map
<String
, Object
> map
) {
213 for (Property property
: getImplicitProperties(proto
)) {
214 addPropertyToMap(property
, true, map
);
218 private static Iterable
<Property
> getImplicitProperties(EntityProto proto
) {
219 return Collections
.singleton(buildImplicitKeyProperty(proto
));
222 private static Property
buildImplicitKeyProperty(EntityProto proto
) {
223 Property keyProp
= new Property();
224 keyProp
.setName(Entity
.KEY_RESERVED_PROPERTY
);
225 PropertyValue propVal
= new PropertyValue();
226 propVal
.setReferenceValue(KeyType
.toReferenceValue(proto
.getKey()));
227 keyProp
.setValue(propVal
);
232 * Locates and returns all indexed properties with the given name on the
233 * given proto. If there are a mix of matching multiple and non-multiple
234 * properties, the collection will contain the first non-multiple property.
235 * @return A list, potentially empty, containing matching properties.
237 public static Collection
<Property
> findIndexedPropertiesOnPb(
238 EntityProto proto
, String propertyName
) {
239 if (propertyName
.equals(Entity
.KEY_RESERVED_PROPERTY
)) {
240 return Collections
.singleton(buildImplicitKeyProperty(proto
));
242 List
<Property
> matchingMultipleProps
= new ArrayList
<>();
243 for (Property prop
: proto
.propertys()) {
244 if (prop
.getName().equals(propertyName
)) {
245 if (!prop
.isMultiple()) {
246 return Collections
.singleton(prop
);
248 matchingMultipleProps
.add(prop
);
252 return matchingMultipleProps
;
255 private static void addPropertyToMap(Property property
, boolean indexed
,
256 Map
<String
, Object
> map
) {
257 String name
= property
.getName();
258 Object value
= getPropertyValue(property
);
260 if (property
.isMultiple()) {
261 @SuppressWarnings({"unchecked"})
262 List
<Object
> results
= (List
<Object
>) PropertyContainer
.unwrapValue(map
.get(name
));
263 if (results
== null) {
264 results
= new ArrayList
<Object
>();
265 map
.put(name
, indexed ? results
: new UnindexedValue(results
));
269 map
.put(name
, indexed ? value
: new UnindexedValue(value
));
274 * Returns the value for the property as its canonical type.
276 * @param property a not {@code null} property
277 * @return {@code null} if no value was set for {@code property}
279 public static Object
getPropertyValue(Property property
) {
280 PropertyValue value
= property
.getValue();
281 for (Type
<?
> type
: typeMap
.values()) {
282 if (type
.isType(property
.getMeaningEnum(), value
)) {
283 return type
.getValue(value
);
290 * @see #addPropertiesToPb(Map, EntityProto)
292 static void addPropertiesToPb(Map
<String
, Object
> map
, EntityV4
.Entity
.Builder proto
) {
293 for (Map
.Entry
<String
, Object
> entry
: map
.entrySet()) {
294 proto
.addProperty(toV4Property(entry
.getKey(), entry
.getValue()));
299 * Copy all of the properties present on {@code proto} into {@code map}.
301 * EntityV4 must know if the proto came from an index-only query as User and GeoPt types
302 * overwrite the INDEX_ONLY meaning.
304 * @param proto the proto from which to extract properties
305 * @param indexOnly if the proto is from an index only query (a projection query)
306 * @param map the map to populate
308 static void extractPropertiesFromPb(EntityV4
.EntityOrBuilder proto
, boolean indexOnly
,
309 Map
<String
, Object
> map
) {
311 for (EntityV4
.PropertyOrBuilder prop
: proto
.getPropertyOrBuilderList()) {
312 map
.put(prop
.getName(), new RawValue(prop
.getValue()));
315 for (EntityV4
.PropertyOrBuilder prop
: proto
.getPropertyOrBuilderList()) {
316 addPropertyToMap(prop
, map
);
321 static EntityV4
.Value
.Builder
toV4Value(Object value
, boolean indexed
) {
323 EntityV4
.Value
.Builder builder
= EntityV4
.Value
.newBuilder();
324 setIndexed(indexed
, builder
);
327 return getType(value
.getClass()).toV4Value(value
, indexed
);
330 private static EntityV4
.Property
.Builder
toV4Property(String name
, Object value
) {
331 EntityV4
.Property
.Builder builder
= EntityV4
.Property
.newBuilder();
332 builder
.setName(name
);
333 boolean indexed
= !(value
instanceof UnindexedValue
);
334 value
= PropertyContainer
.unwrapValue(value
);
335 if (value
instanceof Collection
<?
>) {
336 Collection
<?
> values
= (Collection
<?
>) value
;
337 if (values
.isEmpty()) {
338 builder
.setValue(toV4Value(null, indexed
));
340 EntityV4
.Value
.Builder valueBuilder
= builder
.getValueBuilder();
341 for (Object listValue
: values
) {
342 valueBuilder
.addListValue(toV4Value(listValue
, indexed
));
346 builder
.setValue(toV4Value(value
, indexed
));
351 private static void addPropertyToMap(EntityV4
.PropertyOrBuilder prop
, Map
<String
, Object
> map
) {
352 EntityV4
.ValueOrBuilder value
= prop
.getValueOrBuilder();
353 boolean indexed
= value
.getIndexed();
355 if (value
.getListValueCount() > 0) {
356 ArrayList
<Object
> resultList
= new ArrayList
<Object
>(value
.getListValueCount());
357 for (EntityV4
.ValueOrBuilder subValue
: value
.getListValueOrBuilderList()) {
358 indexed
&= subValue
.getIndexed();
359 if (subValue
.getListValueCount() > 0) {
360 throw new IllegalArgumentException("Invalid Entity PB: list within a list.");
362 resultList
.add(getValue(subValue
));
366 result
= getValue(value
);
370 result
= new UnindexedValue(result
);
372 map
.put(prop
.getName(), result
);
375 private static Object
getValue(EntityV4
.ValueOrBuilder value
) {
376 for (Type
<?
> type
: typeMap
.values()) {
377 if (type
.isType(value
)) {
378 return type
.getValue(value
);
384 private static Meaning
getV3MeaningOf(EntityV4
.ValueOrBuilder value
) {
385 return Meaning
.valueOf(value
.getMeaning());
388 private static AppIdNamespace
toAppIdNamespace(EntityV4
.PartitionIdOrBuilder partitionId
) {
389 return new AppIdNamespace(partitionId
.getDatasetId(),
390 partitionId
.hasNamespace() ? partitionId
.getNamespace() : "");
393 private static EntityV4
.PartitionId
.Builder
toV4PartitionId(AppIdNamespace appNs
) {
394 EntityV4
.PartitionId
.Builder builder
= EntityV4
.PartitionId
.newBuilder();
395 builder
.setDatasetId(appNs
.getAppId());
396 if (!appNs
.getNamespace().isEmpty()) {
397 builder
.setNamespace(appNs
.getNamespace());
402 static EntityV4
.Key
.Builder
toV4Key(Key key
) {
403 EntityV4
.Key
.Builder builder
= EntityV4
.Key
.newBuilder();
404 builder
.setPartitionId(toV4PartitionId(key
.getAppIdNamespace()));
405 List
<EntityV4
.Key
.PathElement
> pathElementList
= new ArrayList
<>();
407 EntityV4
.Key
.PathElement
.Builder pathElement
= EntityV4
.Key
.PathElement
.newBuilder();
408 pathElement
.setKind(key
.getKind());
409 if (key
.getName() != null) {
410 pathElement
.setName(key
.getName());
411 } else if (key
.getId() != Key
.NOT_ASSIGNED
) {
412 pathElement
.setId(key
.getId());
414 pathElementList
.add(pathElement
.build());
415 key
= key
.getParent();
416 } while (key
!= null);
417 builder
.addAllPathElement(Lists
.reverse(pathElementList
));
421 static Key
toKey(EntityV4
.KeyOrBuilder proto
) {
422 AppIdNamespace appIdNamespace
= toAppIdNamespace(proto
.getPartitionId());
423 if (proto
.getPathElementCount() == 0) {
424 throw new IllegalArgumentException("Invalid Key PB: no elements.");
427 for (EntityV4
.Key
.PathElementOrBuilder e
: proto
.getPathElementOrBuilderList()) {
428 String kind
= e
.getKind();
429 if (e
.hasName() && e
.hasId()) {
430 throw new IllegalArgumentException("Invalid Key PB: both id and name are set.");
432 key
= new Key(kind
, key
, e
.getId(), e
.hasName() ? e
.getName() : null, appIdNamespace
);
437 static Entity
toEntity(EntityV4
.EntityOrBuilder v4Entity
) {
438 Entity entity
= new Entity(DataTypeTranslator
.toKey(v4Entity
.getKey()));
439 DataTypeTranslator
.extractPropertiesFromPb(v4Entity
, false,
440 entity
.getPropertyMap());
444 static Entity
toEntity(EntityV4
.EntityOrBuilder v4Entity
, Collection
<Projection
> projections
) {
445 Entity entity
= new Entity(DataTypeTranslator
.toKey(v4Entity
.getKey()));
447 Map
<String
, Object
> values
= Maps
.newHashMap();
448 DataTypeTranslator
.extractPropertiesFromPb(v4Entity
, true, values
);
449 for (Projection projection
: projections
) {
450 entity
.setProperty(projection
.getName(), projection
.getValue(values
));
455 static EntityV4
.Entity
.Builder
toV4Entity(Entity entity
) {
456 EntityV4
.Entity
.Builder v4Entity
= EntityV4
.Entity
.newBuilder();
457 v4Entity
.setKey(toV4Key(entity
.getKey()));
458 addPropertiesToPb(entity
.getPropertyMap(), v4Entity
);
463 * Returns the value for the property as its comparable representation type.
465 * @param property a not {@code null} property
466 * @return {@code null} if no value was set for {@code property}
468 @SuppressWarnings("unchecked")
469 public static Comparable
<Object
> getComparablePropertyValue(Property property
) {
470 return (Comparable
<Object
>) RAW_VALUE_TYPE
.asComparable(new RawValue(property
.getValue()));
474 * Converts the given {@link Object} into a supported value then returns it as
475 * a comparable object so it can be compared to other data types.
477 * @param value any Object that can be converted into a supported DataType
478 * @return {@code null} if value is null
479 * @throws UnsupportedOperationException if value is not supported
481 @SuppressWarnings("unchecked")
482 static Comparable
<Object
> getComparablePropertyValue(Object value
) {
483 return value
== null ?
null
484 : (Comparable
<Object
>) getType(value
.getClass()).asComparable(value
);
488 * Get the rank of the given datastore type relative to other datastore
489 * types. Note that datastore types do not necessarily have unique ranks.
491 @SuppressWarnings({"unchecked", "rawtypes"})
492 public static int getTypeRank(Class
<?
extends Comparable
> datastoreType
) {
493 return comparableTypeMap
.get(datastoreType
);
497 * Gets the {@link Type} that knows how to translate objects of
498 * type {@code clazz} into protocol buffers that the data store can
500 * @throws UnsupportedOperationException if clazz is not supported
502 @SuppressWarnings("unchecked")
503 private static <T
> Type
<T
> getType(Class
<T
> clazz
) {
504 if (typeMap
.containsKey(clazz
)) {
505 return (Type
<T
>) typeMap
.get(clazz
);
507 throw new UnsupportedOperationException("Unsupported data type: " + clazz
.getName());
512 * {@code Type} is an abstract class that knows how to convert Java
513 * objects of one or more types into datastore representations.
515 * @param <T> The canonical Java class for this type.
517 abstract static class Type
<T
> {
519 * @returns {@code true} if the given meaning and property value matches this {@link Type}.
521 public final boolean isType(Meaning meaning
, PropertyValue propertyValue
) {
522 return meaning
== getV3Meaning() && hasValue(propertyValue
);
526 * @returns {@code true} if the given value matches this {@link Type}.
528 public boolean isType(EntityV4
.ValueOrBuilder propertyValue
) {
529 return getV3MeaningOf(propertyValue
) == getV3Meaning() && hasValue(propertyValue
);
533 * Returns the {@link Comparable} for the given value, or
534 * {@code null} if values of this type are not comparable.
536 public abstract Comparable
<?
> asComparable(Object value
);
539 * Sets the value of {@code propertyValue} to {@code value}.
540 * @returns if the value is indexable
542 public abstract boolean toV3Value(Object value
, PropertyValue propertyValue
);
545 * Returns a new V4 Value for the given parameters.
547 * @param value the Java native value to convert
548 * @param indexed the desired indexing, ignored for types that are not indexable
549 * @returns the EntityV4 representation of the given value and desired indexing
551 public abstract EntityV4
.Value
.Builder
toV4Value(Object value
, boolean indexed
);
554 * Returns the value of {@code propertyValue} as its canonical Java type.
556 * Use {@link #isType} first to determine if the property has a value of the given type.
558 * @param propertyValue a not {@code null} value representing this {@code Type}
559 * @return the canonical Java representation of {@code propertyValue}.
561 public abstract T
getValue(PropertyValue propertyValue
);
564 * @see Type#getValue(PropertyValue)
566 public abstract T
getValue(EntityV4
.ValueOrBuilder propertyValue
);
569 * @returns {@code true} if a value of this {@code Type} is set on the given propertyValue.
571 public abstract boolean hasValue(PropertyValue propertyValue
);
574 * @returns {@code true} if a value of this {@code Type} is set on the given propertyValue.
576 public abstract boolean hasValue(EntityV4
.ValueOrBuilder propertyValue
);
579 * @returns the {@link Meaning} for this {@link Type}
581 protected Meaning
getV3Meaning() {
582 return Meaning
.NO_MEANING
;
587 * A base class with common functions for types that have the same datastore representation.
589 * @param <S> the datastore type
590 * @param <T> the canonical Java class for this type
592 private abstract static class BaseVariantType
<S
, T
> extends Type
<T
> {
594 * @returns the datastore representation of the given value
596 protected abstract S
toDatastoreValue(Object value
);
598 * @returns the native representation of the given value
600 protected abstract T
fromDatastoreValue(S datastoreValue
);
604 * Base class for types that store strings in the datastore.
606 * @param <T> the canonical Java class for this type
608 private abstract static class BaseStringType
<T
> extends BaseVariantType
<String
, T
> {
610 public boolean toV3Value(Object value
, PropertyValue propertyValue
) {
611 propertyValue
.setStringValue(toDatastoreValue(value
));
616 public EntityV4
.Value
.Builder
toV4Value(Object value
, boolean indexed
) {
617 EntityV4
.Value
.Builder builder
= EntityV4
.Value
.newBuilder();
618 builder
.setStringValue(toDatastoreValue(value
));
619 setIndexed(indexed
, builder
);
620 setMeaning(getV3Meaning().getValue(), builder
);
625 public final T
getValue(PropertyValue propertyValue
) {
626 return fromDatastoreValue(propertyValue
.getStringValue());
630 public T
getValue(EntityV4
.ValueOrBuilder propertyValue
) {
631 return fromDatastoreValue(propertyValue
.getStringValue());
635 public final boolean hasValue(PropertyValue propertyValue
) {
636 return propertyValue
.hasStringValue();
640 public boolean hasValue(EntityV4
.ValueOrBuilder propertyValue
) {
641 return propertyValue
.hasStringValue();
645 public ComparableByteArray
asComparable(Object value
) {
646 return new ComparableByteArray(ProtocolSupport
.toBytesUtf8(toDatastoreValue(value
)));
651 * Base class for types that store bytes in the datastore.
653 * @param <T> the canonical Java class for this type
655 private abstract static class BaseBlobType
<T
> extends BaseVariantType
<byte[], T
> {
656 protected abstract boolean isIndexable();
659 public final boolean hasValue(PropertyValue propertyValue
) {
660 return propertyValue
.hasStringValue();
664 public final boolean toV3Value(Object value
, PropertyValue propertyValue
) {
665 propertyValue
.setStringValueAsBytes(toDatastoreValue(value
));
666 return isIndexable();
670 public EntityV4
.Value
.Builder
toV4Value(Object value
, boolean indexed
) {
671 EntityV4
.Value
.Builder builder
= EntityV4
.Value
.newBuilder();
672 builder
.setBlobValue(ByteString
.copyFrom(toDatastoreValue(value
)));
673 setIndexed(indexed
&& isIndexable(), builder
);
678 public final T
getValue(PropertyValue propertyValue
) {
679 return fromDatastoreValue(propertyValue
.getStringValueAsBytes());
683 public final ComparableByteArray
asComparable(Object value
) {
684 return isIndexable() ?
new ComparableByteArray(toDatastoreValue(value
)) : null;
689 * Base class for types that store predefined entities in V4.
691 * @param <T> the canonical Java class for this type
693 private abstract static class BasePredefinedEntityType
<T
> extends Type
<T
> {
695 * @returns the predefined entity meaning to use in V4
697 protected abstract int getV4Meaning();
700 * @returns the V4 Entity representation for the given value
702 protected abstract EntityV4
.Entity
getEntity(Object value
);
705 public final boolean isType(EntityV4
.ValueOrBuilder propertyValue
) {
706 return propertyValue
.getMeaning() == getV4Meaning() && hasValue(propertyValue
);
710 public final boolean hasValue(EntityV4
.ValueOrBuilder propertyValue
) {
711 return propertyValue
.hasEntityValue();
715 public final EntityV4
.Value
.Builder
toV4Value(Object value
, boolean indexed
) {
716 EntityV4
.Value
.Builder builder
= EntityV4
.Value
.newBuilder();
717 builder
.setEntityValue(getEntity(value
));
718 setIndexed(indexed
, builder
);
719 setMeaning(getV4Meaning(), builder
);
725 * Returns the V4 property representation for the given name and value, unindexed.
727 private static EntityV4
.Property
makeUnindexedProperty(String name
, double value
) {
728 return EntityV4
.Property
.newBuilder()
730 .setValue(EntityV4
.Value
.newBuilder().setDoubleValue(value
).setIndexed(false))
735 * Returns the V4 property representation for the given name and value, unindexed.
737 private static EntityV4
.Property
makeUnindexedProperty(String name
, String value
) {
738 return EntityV4
.Property
.newBuilder()
740 .setValue(EntityV4
.Value
.newBuilder().setStringValue(value
).setIndexed(false))
745 * Base class for types that int64 values in the datastore.
747 * @param <T> the canonical Java class for this type
749 private abstract static class BaseInt64Type
<T
> extends BaseVariantType
<Long
, T
> {
751 public final boolean toV3Value(Object value
, PropertyValue propertyValue
) {
752 propertyValue
.setInt64Value(toDatastoreValue(value
));
757 public EntityV4
.Value
.Builder
toV4Value(Object value
, boolean indexed
) {
758 EntityV4
.Value
.Builder builder
= EntityV4
.Value
.newBuilder();
759 builder
.setIntegerValue(toDatastoreValue(value
));
760 setIndexed(indexed
, builder
);
761 setMeaning(getV3Meaning().getValue(), builder
);
766 public T
getValue(PropertyValue propertyValue
) {
767 return fromDatastoreValue(propertyValue
.getInt64Value());
771 public T
getValue(EntityV4
.ValueOrBuilder propertyValue
) {
772 return fromDatastoreValue(propertyValue
.getIntegerValue());
776 public boolean hasValue(PropertyValue propertyValue
) {
777 return propertyValue
.hasInt64Value();
781 public boolean hasValue(EntityV4
.ValueOrBuilder propertyValue
) {
782 return propertyValue
.hasIntegerValue();
786 public Long
asComparable(Object value
) {
787 return toDatastoreValue(value
);
792 * The type for projected index values.
794 private static final class RawValueType
extends Type
<RawValue
> {
796 public Meaning
getV3Meaning() {
797 return Meaning
.INDEX_VALUE
;
801 public boolean hasValue(PropertyValue propertyValue
) {
806 public boolean hasValue(EntityV4
.ValueOrBuilder propertyValue
) {
811 public boolean toV3Value(Object value
, PropertyValue propertyValue
) {
812 throw new UnsupportedOperationException();
816 public EntityV4
.Value
.Builder
toV4Value(Object value
, boolean indexed
) {
817 throw new UnsupportedOperationException();
821 public RawValue
getValue(PropertyValue propertyValue
) {
822 return new RawValue(propertyValue
);
826 public RawValue
getValue(EntityV4
.ValueOrBuilder propertyValue
) {
827 EntityV4
.Value value
= null;
828 if (propertyValue
instanceof EntityV4
.Value
) {
829 value
= (EntityV4
.Value
) propertyValue
;
831 value
= ((EntityV4
.Value
.Builder
) propertyValue
).build();
833 return new RawValue(value
);
836 @SuppressWarnings("unchecked")
838 public Comparable
<?
> asComparable(Object value
) {
839 value
= ((RawValue
) value
).getValue();
840 if (value
instanceof byte[]) {
841 return new ComparableByteArray((byte[]) value
);
843 return (Comparable
<?
>) value
;
848 * The raw String type.
850 private static final class StringType
extends BaseStringType
<String
> {
852 protected String
toDatastoreValue(Object value
) {
853 return value
.toString();
857 protected String
fromDatastoreValue(String datastoreValue
) {
858 return datastoreValue
;
863 * The raw int64 type.
865 private static final class Int64Type
extends BaseInt64Type
<Long
> {
867 protected Long
toDatastoreValue(Object value
) {
868 return ((Number
) value
).longValue();
872 protected Long
fromDatastoreValue(Long datastoreValue
) {
873 return datastoreValue
;
878 * The raw double type.
880 private static final class DoubleType
extends Type
<Double
> {
882 public boolean toV3Value(Object value
, PropertyValue propertyValue
) {
883 propertyValue
.setDoubleValue(((Number
) value
).doubleValue());
888 public EntityV4
.Value
.Builder
toV4Value(Object value
, boolean indexed
) {
889 EntityV4
.Value
.Builder builder
= EntityV4
.Value
.newBuilder();
890 builder
.setDoubleValue(((Number
) value
).doubleValue());
891 setIndexed(indexed
, builder
);
896 public Double
getValue(PropertyValue propertyValue
) {
897 return propertyValue
.getDoubleValue();
901 public Double
getValue(EntityV4
.ValueOrBuilder propertyValue
) {
902 return propertyValue
.getDoubleValue();
906 public boolean hasValue(PropertyValue propertyValue
) {
907 return propertyValue
.hasDoubleValue();
911 public boolean hasValue(EntityV4
.ValueOrBuilder propertyValue
) {
912 return propertyValue
.hasDoubleValue();
916 public Double
asComparable(Object value
) {
917 return ((Number
) value
).doubleValue();
922 * The raw boolean type.
924 private static final class BoolType
extends Type
<Boolean
> {
926 public boolean toV3Value(Object value
, PropertyValue propertyValue
) {
927 propertyValue
.setBooleanValue((Boolean
) value
);
932 public EntityV4
.Value
.Builder
toV4Value(Object value
, boolean indexed
) {
933 EntityV4
.Value
.Builder builder
= EntityV4
.Value
.newBuilder();
934 builder
.setBooleanValue((Boolean
) value
);
935 setIndexed(indexed
, builder
);
940 public Boolean
getValue(PropertyValue propertyValue
) {
941 return propertyValue
.isBooleanValue();
945 public Boolean
getValue(EntityV4
.ValueOrBuilder propertyValue
) {
946 return propertyValue
.getBooleanValue();
950 public boolean hasValue(PropertyValue propertyValue
) {
951 return propertyValue
.hasBooleanValue();
955 public boolean hasValue(EntityV4
.ValueOrBuilder propertyValue
) {
956 return propertyValue
.hasBooleanValue();
960 public Boolean
asComparable(Object value
) {
961 return (Boolean
) value
;
968 * Stored as an entity with a special meaning in V4.
970 private static final class UserType
extends BasePredefinedEntityType
<User
> {
971 public static final int MEANING_PREDEFINED_ENTITY_USER
= 20;
972 public static final String PROPERTY_NAME_EMAIL
= "email";
973 public static final String PROPERTY_NAME_AUTH_DOMAIN
= "auth_domain";
974 public static final String PROPERTY_NAME_USER_ID
= "user_id";
977 public int getV4Meaning() {
978 return MEANING_PREDEFINED_ENTITY_USER
;
982 public EntityV4
.Entity
getEntity(Object value
) {
983 User user
= (User
) value
;
984 EntityV4
.Entity
.Builder builder
= EntityV4
.Entity
.newBuilder();
985 builder
.addProperty(makeUnindexedProperty(PROPERTY_NAME_EMAIL
, user
.getEmail()));
986 builder
.addProperty(makeUnindexedProperty(PROPERTY_NAME_AUTH_DOMAIN
, user
.getAuthDomain()));
987 if (user
.getUserId() != null) {
988 builder
.addProperty(makeUnindexedProperty(PROPERTY_NAME_USER_ID
, user
.getUserId()));
990 return builder
.build();
994 public boolean toV3Value(Object value
, PropertyValue propertyValue
) {
995 User user
= (User
) value
;
996 UserValue userValue
= new UserValue();
997 userValue
.setEmail(user
.getEmail());
998 userValue
.setAuthDomain(user
.getAuthDomain());
999 if (user
.getUserId() != null) {
1000 userValue
.setObfuscatedGaiaid(user
.getUserId());
1002 userValue
.setGaiaid(0);
1003 propertyValue
.setUserValue(userValue
);
1008 public User
getValue(PropertyValue propertyValue
) {
1009 UserValue userValue
= propertyValue
.getUserValue();
1010 String userId
= userValue
.hasObfuscatedGaiaid() ? userValue
.getObfuscatedGaiaid() : null;
1011 return new User(userValue
.getEmail(), userValue
.getAuthDomain(), userId
);
1015 public User
getValue(EntityV4
.ValueOrBuilder propertyValue
) {
1017 String authDomain
= "";
1018 String userId
= null;
1019 for (EntityV4
.PropertyOrBuilder prop
:
1020 propertyValue
.getEntityValueOrBuilder().getPropertyOrBuilderList()) {
1021 if (prop
.getName().equals(PROPERTY_NAME_EMAIL
)) {
1022 email
= prop
.getValueOrBuilder().getStringValue();
1023 } else if (prop
.getName().equals(PROPERTY_NAME_AUTH_DOMAIN
)) {
1024 authDomain
= prop
.getValueOrBuilder().getStringValue();
1025 } else if (prop
.getName().equals(PROPERTY_NAME_USER_ID
)) {
1026 userId
= prop
.getValueOrBuilder().getStringValue();
1029 return new User(email
, authDomain
, userId
);
1033 public boolean hasValue(PropertyValue propertyValue
) {
1034 return propertyValue
.hasUserValue();
1038 public final Comparable
<User
> asComparable(Object value
) {
1039 return (User
) value
;
1046 * Stored as a GeoPoint value with no meaning in V4.
1048 * TODO(user): The above comment isn't completely accurate until we have switched the
1049 * internal format to V1BETA3.
1051 private static class GeoPtType
extends Type
<GeoPt
> {
1052 public static final int MEANING_PREDEFINED_ENTITY_GEORSS_POINT
= 9;
1053 public static final String PROPERTY_NAME_X
= "x";
1054 public static final String PROPERTY_NAME_Y
= "y";
1057 public boolean isType(EntityV4
.ValueOrBuilder propertyValue
) {
1058 if (propertyValue
.hasGeoPointValue() && !propertyValue
.hasMeaning()) {
1060 } else if (propertyValue
.hasEntityValue()
1061 && propertyValue
.getMeaning() == MEANING_PREDEFINED_ENTITY_GEORSS_POINT
) {
1069 public boolean toV3Value(Object value
, PropertyValue propertyValue
) {
1070 GeoPt geoPt
= (GeoPt
) value
;
1071 PropertyValue
.PointValue pv
= new PropertyValue
.PointValue()
1072 .setX(geoPt
.getLatitude())
1073 .setY(geoPt
.getLongitude());
1074 propertyValue
.setPointValue(pv
);
1079 public final EntityV4
.Value
.Builder
toV4Value(Object value
, boolean indexed
) {
1080 GeoPt geoPt
= (GeoPt
) value
;
1081 EntityV4
.Value
.Builder builder
= EntityV4
.Value
.newBuilder();
1082 builder
.getEntityValueBuilder()
1083 .addProperty(makeUnindexedProperty(PROPERTY_NAME_X
, geoPt
.getLatitude()))
1084 .addProperty(makeUnindexedProperty(PROPERTY_NAME_Y
, geoPt
.getLongitude()))
1086 setIndexed(indexed
, builder
);
1087 setMeaning(MEANING_PREDEFINED_ENTITY_GEORSS_POINT
, builder
);
1092 public GeoPt
getValue(PropertyValue propertyValue
) {
1093 PropertyValue
.PointValue pv
= propertyValue
.getPointValue();
1094 return new GeoPt((float) pv
.getX(), (float) pv
.getY());
1098 public GeoPt
getValue(EntityV4
.ValueOrBuilder propertyValue
) {
1101 for (EntityV4
.PropertyOrBuilder prop
:
1102 propertyValue
.getEntityValueOrBuilder().getPropertyOrBuilderList()) {
1103 if (prop
.getName().equals(PROPERTY_NAME_X
)) {
1104 x
= prop
.getValueOrBuilder().getDoubleValue();
1105 } else if (prop
.getName().equals(PROPERTY_NAME_Y
)) {
1106 y
= prop
.getValueOrBuilder().getDoubleValue();
1109 if (propertyValue
.hasGeoPointValue()) {
1110 x
= propertyValue
.getGeoPointValue().getLatitude();
1111 y
= propertyValue
.getGeoPointValue().getLongitude();
1113 return new GeoPt((float) x
, (float) y
);
1117 public boolean hasValue(PropertyValue propertyValue
) {
1118 return propertyValue
.hasPointValue();
1122 public final boolean hasValue(EntityV4
.ValueOrBuilder propertyValue
) {
1123 return propertyValue
.hasGeoPointValue() || propertyValue
.hasEntityValue();
1127 public Meaning
getV3Meaning() {
1128 return Meaning
.GEORSS_POINT
;
1132 public final Comparable
<GeoPt
> asComparable(Object value
) {
1133 return (GeoPt
) value
;
1138 * The key/reference type.
1140 private static final class KeyType
extends Type
<Key
> {
1142 public boolean toV3Value(Object value
, PropertyValue propertyValue
) {
1143 Reference keyRef
= KeyTranslator
.convertToPb((Key
) value
);
1144 propertyValue
.setReferenceValue(toReferenceValue(keyRef
));
1149 public EntityV4
.Value
.Builder
toV4Value(Object value
, boolean indexed
) {
1150 EntityV4
.Value
.Builder builder
= EntityV4
.Value
.newBuilder();
1151 builder
.setKeyValue(toV4Key((Key
) value
));
1152 setIndexed(indexed
, builder
);
1157 public Key
getValue(PropertyValue propertyValue
) {
1158 return KeyTranslator
.createFromPb(toReference(propertyValue
.getReferenceValue()));
1162 public Key
getValue(EntityV4
.ValueOrBuilder propertyValue
) {
1163 return toKey(propertyValue
.getKeyValue());
1167 public boolean hasValue(PropertyValue propertyValue
) {
1168 return propertyValue
.hasReferenceValue();
1172 public boolean hasValue(EntityV4
.ValueOrBuilder propertyValue
) {
1173 return propertyValue
.hasKeyValue();
1177 public Key
asComparable(Object value
) {
1181 private static ReferenceValue
toReferenceValue(Reference keyRef
) {
1182 ReferenceValue refValue
= new ReferenceValue();
1183 refValue
.setApp(keyRef
.getApp());
1184 if (keyRef
.hasNameSpace()) {
1185 refValue
.setNameSpace(keyRef
.getNameSpace());
1187 Path path
= keyRef
.getPath();
1188 for (Element element
: path
.elements()) {
1189 ReferenceValuePathElement newElement
= new ReferenceValuePathElement();
1190 newElement
.setType(element
.getType());
1191 if (element
.hasName()) {
1192 newElement
.setName(element
.getName());
1194 if (element
.hasId()) {
1195 newElement
.setId(element
.getId());
1197 refValue
.addPathElement(newElement
);
1203 private static Reference
toReference(ReferenceValue refValue
) {
1204 Reference reference
= new Reference();
1205 reference
.setApp(refValue
.getApp());
1206 if (refValue
.hasNameSpace()) {
1207 reference
.setNameSpace(refValue
.getNameSpace());
1209 Path path
= new Path();
1210 for (ReferenceValuePathElement element
: refValue
.pathElements()) {
1211 Element newElement
= new Element();
1212 newElement
.setType(element
.getType());
1213 if (element
.hasName()) {
1214 newElement
.setName(element
.getName());
1216 if (element
.hasId()) {
1217 newElement
.setId(element
.getId());
1219 path
.addElement(newElement
);
1221 reference
.setPath(path
);
1227 * The non-indexable blob type.
1229 private static class BlobType
extends BaseBlobType
<Blob
> {
1231 public Meaning
getV3Meaning() {
1232 return Meaning
.BLOB
;
1236 public boolean isType(EntityV4
.ValueOrBuilder propertyValue
) {
1237 return getV3MeaningOf(propertyValue
) == Meaning
.NO_MEANING
1238 && propertyValue
.getIndexed() == false
1239 && hasValue(propertyValue
);
1243 public boolean hasValue(EntityV4
.ValueOrBuilder propertyValue
) {
1244 return propertyValue
.hasBlobValue();
1248 public Blob
getValue(EntityV4
.ValueOrBuilder propertyValue
) {
1249 return fromDatastoreValue(propertyValue
.getBlobValue().toByteArray());
1253 protected Blob
fromDatastoreValue(byte[] datastoreValue
) {
1254 return new Blob(datastoreValue
);
1258 protected byte[] toDatastoreValue(Object value
) {
1259 return ((Blob
) value
).getBytes();
1263 public boolean isIndexable() {
1269 * The indexable blob type.
1271 private static class ShortBlobType
extends BaseBlobType
<ShortBlob
> {
1273 public Meaning
getV3Meaning() {
1274 return Meaning
.BYTESTRING
;
1278 public boolean isType(EntityV4
.ValueOrBuilder propertyValue
) {
1279 if (!hasValue(propertyValue
)) {
1283 if (propertyValue
.getIndexed()) {
1284 return getV3MeaningOf(propertyValue
) == Meaning
.NO_MEANING
;
1286 return getV3MeaningOf(propertyValue
) == getV3Meaning();
1291 public EntityV4
.Value
.Builder
toV4Value(Object value
, boolean indexed
) {
1292 EntityV4
.Value
.Builder builder
= super.toV4Value(value
, indexed
);
1294 builder
.setMeaning(getV3Meaning().getValue());
1300 public boolean hasValue(EntityV4
.ValueOrBuilder propertyValue
) {
1301 return propertyValue
.hasBlobValue()
1302 || (getV3MeaningOf(propertyValue
) == Meaning
.INDEX_VALUE
1303 && propertyValue
.hasStringValue());
1307 public ShortBlob
getValue(EntityV4
.ValueOrBuilder propertyValue
) {
1308 if (getV3MeaningOf(propertyValue
) == Meaning
.INDEX_VALUE
&& propertyValue
.hasStringValue()) {
1309 return fromDatastoreValue(propertyValue
.getStringValueBytes().toByteArray());
1311 return fromDatastoreValue(propertyValue
.getBlobValue().toByteArray());
1316 protected byte[] toDatastoreValue(Object value
) {
1317 return ((ShortBlob
) value
).getBytes();
1321 protected ShortBlob
fromDatastoreValue(byte[] datastoreValue
) {
1322 return new ShortBlob(datastoreValue
);
1326 public boolean isIndexable() {
1334 * Stored as a partially serialized EntityProto in V3.
1336 private static final class EmbeddedEntityType
extends Type
<EmbeddedEntity
> {
1338 public Meaning
getV3Meaning() {
1339 return Meaning
.ENTITY_PROTO
;
1343 public boolean isType(EntityV4
.ValueOrBuilder propertyValue
) {
1344 return getV3MeaningOf(propertyValue
) == Meaning
.NO_MEANING
1345 && hasValue(propertyValue
);
1349 public boolean hasValue(PropertyValue propertyValue
) {
1350 return propertyValue
.hasStringValue();
1354 public boolean hasValue(EntityV4
.ValueOrBuilder propertyValue
) {
1355 return propertyValue
.hasEntityValue();
1359 public EmbeddedEntity
getValue(PropertyValue propertyValue
) {
1360 EntityProto proto
= new EntityProto();
1361 proto
.mergeFrom(propertyValue
.getStringValueAsBytes());
1362 EmbeddedEntity result
= new EmbeddedEntity();
1363 if (proto
.hasKey() && !proto
.getKey().getApp().isEmpty()) {
1364 result
.setKey(KeyTranslator
.createFromPb(proto
.getKey()));
1366 extractPropertiesFromPb(proto
, result
.getPropertyMap());
1371 public EmbeddedEntity
getValue(EntityV4
.ValueOrBuilder propertyValue
) {
1372 EmbeddedEntity result
= new EmbeddedEntity();
1373 EntityV4
.Entity proto
= propertyValue
.getEntityValue();
1374 if (proto
.hasKey()) {
1375 result
.setKey(toKey(proto
.getKey()));
1377 extractPropertiesFromPb(proto
, false, result
.getPropertyMap());
1382 public boolean toV3Value(Object value
, PropertyValue propertyValue
) {
1383 EmbeddedEntity structProp
= (EmbeddedEntity
) value
;
1384 EntityProto proto
= new EntityProto();
1385 if (structProp
.getKey() != null) {
1386 proto
.setKey(KeyTranslator
.convertToPb(structProp
.getKey()));
1388 addPropertiesToPb(structProp
.getPropertyMap(), proto
);
1389 propertyValue
.setStringValueAsBytes(proto
.toByteArray());
1394 public EntityV4
.Value
.Builder
toV4Value(Object value
, boolean indexed
) {
1395 EmbeddedEntity structProp
= (EmbeddedEntity
) value
;
1396 EntityV4
.Value
.Builder builder
= EntityV4
.Value
.newBuilder();
1397 EntityV4
.Entity
.Builder proto
= builder
.getEntityValueBuilder();
1398 if (structProp
.getKey() != null) {
1399 proto
.setKey(toV4Key(structProp
.getKey()));
1401 addPropertiesToPb(structProp
.getPropertyMap(), proto
);
1402 builder
.setIndexed(false);
1407 public Comparable
<?
> asComparable(Object value
) {
1413 * The non-indexable {@link Text} type.
1415 private static final class TextType
extends BaseStringType
<Text
> {
1417 public Meaning
getV3Meaning() {
1418 return Meaning
.TEXT
;
1422 public boolean toV3Value(Object value
, PropertyValue propertyValue
) {
1423 super.toV3Value(value
, propertyValue
);
1428 public EntityV4
.Value
.Builder
toV4Value(Object value
, boolean indexed
) {
1429 return super.toV4Value(value
, false);
1433 protected Text
fromDatastoreValue(String datastoreString
) {
1434 return new Text(datastoreString
);
1438 protected String
toDatastoreValue(Object value
) {
1439 return ((Text
) value
).getValue();
1443 public ComparableByteArray
asComparable(Object value
) {
1449 * The {@link BlobKey} type.
1451 * In V3 dates are just strings with a special meaning.
1453 private static final class BlobKeyType
extends BaseStringType
<BlobKey
> {
1455 public Meaning
getV3Meaning() {
1456 return Meaning
.BLOBKEY
;
1460 public boolean isType(EntityV4
.ValueOrBuilder propertyValue
) {
1461 return getV3MeaningOf(propertyValue
) == Meaning
.NO_MEANING
1462 && hasValue(propertyValue
);
1466 public boolean hasValue(EntityV4
.ValueOrBuilder propertyValue
) {
1467 return propertyValue
.hasBlobKeyValue()
1468 || (getV3MeaningOf(propertyValue
) == Meaning
.INDEX_VALUE
1469 && propertyValue
.hasStringValue());
1473 public EntityV4
.Value
.Builder
toV4Value(Object value
, boolean indexed
) {
1474 EntityV4
.Value
.Builder builder
= EntityV4
.Value
.newBuilder();
1475 builder
.setBlobKeyValue(toDatastoreValue(value
));
1476 setIndexed(indexed
, builder
);
1481 public BlobKey
getValue(EntityV4
.ValueOrBuilder propertyValue
) {
1482 if (getV3MeaningOf(propertyValue
) == Meaning
.INDEX_VALUE
&& propertyValue
.hasStringValue()) {
1483 return fromDatastoreValue(propertyValue
.getStringValue());
1485 return fromDatastoreValue(propertyValue
.getBlobKeyValue());
1490 protected String
toDatastoreValue(Object value
) {
1491 return ((BlobKey
) value
).getKeyString();
1495 protected BlobKey
fromDatastoreValue(String datastoreString
) {
1496 return new BlobKey(datastoreString
);
1503 * In V3 dates are just int64s with a special meaning.
1505 private static final class DateType
extends BaseInt64Type
<Date
> {
1507 public Meaning
getV3Meaning() {
1508 return Meaning
.GD_WHEN
;
1512 public boolean isType(EntityV4
.ValueOrBuilder propertyValue
) {
1513 return propertyValue
.getMeaning() == 0 && hasValue(propertyValue
);
1517 public boolean hasValue(EntityV4
.ValueOrBuilder propertyValue
) {
1518 return propertyValue
.hasTimestampMicrosecondsValue()
1519 || (getV3MeaningOf(propertyValue
) == Meaning
.INDEX_VALUE
1520 && propertyValue
.hasIntegerValue());
1524 public EntityV4
.Value
.Builder
toV4Value(Object value
, boolean indexed
) {
1525 EntityV4
.Value
.Builder builder
= EntityV4
.Value
.newBuilder();
1526 builder
.setTimestampMicrosecondsValue(toDatastoreValue(value
));
1527 setIndexed(indexed
, builder
);
1532 public Date
getValue(EntityV4
.ValueOrBuilder propertyValue
) {
1533 if (getV3MeaningOf(propertyValue
) == Meaning
.INDEX_VALUE
&& propertyValue
.hasIntegerValue()) {
1534 return fromDatastoreValue(propertyValue
.getIntegerValue());
1536 return fromDatastoreValue(propertyValue
.getTimestampMicrosecondsValue());
1541 protected Long
toDatastoreValue(Object value
) {
1542 return ((Date
) value
).getTime() * 1000L;
1546 protected Date
fromDatastoreValue(Long datastoreValue
) {
1547 return new Date(datastoreValue
/ 1000L);
1552 * Internally a link is just a string with a special meaning.
1554 private static final class LinkType
extends BaseStringType
<Link
> {
1556 public Meaning
getV3Meaning() {
1557 return Meaning
.ATOM_LINK
;
1561 protected String
toDatastoreValue(Object value
) {
1562 return ((Link
) value
).getValue();
1566 protected Link
fromDatastoreValue(String datastoreValue
) {
1567 return new Link(datastoreValue
);
1572 * Internally a category is just a string with a special meaning.
1574 private static final class CategoryType
extends BaseStringType
<Category
> {
1576 public Meaning
getV3Meaning() {
1577 return Meaning
.ATOM_CATEGORY
;
1581 protected String
toDatastoreValue(Object value
) {
1582 return ((Category
) value
).getCategory();
1586 protected Category
fromDatastoreValue(String datastoreString
) {
1587 return new Category(datastoreString
);
1592 * Internally a rating is just an int64 with a special meaning.
1594 private static final class RatingType
extends BaseInt64Type
<Rating
> {
1596 public Meaning
getV3Meaning() {
1597 return Meaning
.GD_RATING
;
1601 protected Long
toDatastoreValue(Object value
) {
1602 return (long) ((Rating
) value
).getRating();
1606 protected Rating
fromDatastoreValue(Long datastoreLong
) {
1607 return new Rating(datastoreLong
.intValue());
1612 * Internally an email is just a string with a special meaning.
1614 private static final class EmailType
extends BaseStringType
<Email
> {
1616 public Meaning
getV3Meaning() {
1617 return Meaning
.GD_EMAIL
;
1621 protected String
toDatastoreValue(Object value
) {
1622 return ((Email
) value
).getEmail();
1626 protected Email
fromDatastoreValue(String datastoreString
) {
1627 return new Email(datastoreString
);
1632 * Internally a postal address is just a string with a special meaning.
1634 private static final class PostalAddressType
extends BaseStringType
<PostalAddress
> {
1636 public Meaning
getV3Meaning() {
1637 return Meaning
.GD_POSTALADDRESS
;
1641 protected String
toDatastoreValue(Object value
) {
1642 return ((PostalAddress
) value
).getAddress();
1646 protected PostalAddress
fromDatastoreValue(String datastoreString
) {
1647 return new PostalAddress(datastoreString
);
1652 * Internally a phone number is just a string with a special meaning.
1654 private static final class PhoneNumberType
extends BaseStringType
<PhoneNumber
> {
1656 public Meaning
getV3Meaning() {
1657 return Meaning
.GD_PHONENUMBER
;
1661 protected String
toDatastoreValue(Object value
) {
1662 return ((PhoneNumber
) value
).getNumber();
1666 protected PhoneNumber
fromDatastoreValue(String datastoreString
) {
1667 return new PhoneNumber(datastoreString
);
1672 * Internally an IM handle is just a string with a special meaning and a
1673 * well known format.
1675 private static final class IMHandleType
extends BaseStringType
<IMHandle
> {
1677 public Meaning
getV3Meaning() {
1678 return Meaning
.GD_IM
;
1682 protected String
toDatastoreValue(Object value
) {
1683 return ((IMHandle
) value
).toDatastoreString();
1687 protected IMHandle
fromDatastoreValue(String datastoreString
) {
1688 return IMHandle
.fromDatastoreString(datastoreString
);
1692 static Map
<Class
<?
>, Type
<?
>> getTypeMap() {
1697 * A wrapper for a {@code byte[]} that implements {@link Comparable}.
1698 * Comparison algorithm is the same as the prod datastore.
1700 public static final class ComparableByteArray
implements Comparable
<ComparableByteArray
> {
1701 private final byte[] bytes
;
1703 public ComparableByteArray(byte[] bytes
) {
1708 public int compareTo(ComparableByteArray other
) {
1709 byte[] otherBytes
= other
.bytes
;
1710 for (int i
= 0; i
< Math
.min(bytes
.length
, otherBytes
.length
); i
++) {
1711 int v1
= bytes
[i
] & 0xFF;
1712 int v2
= otherBytes
[i
] & 0xFF;
1717 return bytes
.length
- otherBytes
.length
;
1721 public boolean equals(Object obj
) {
1725 return Arrays
.equals(bytes
, ((ComparableByteArray
) obj
).bytes
);
1729 public int hashCode() {
1731 for (byte b
: bytes
) {
1732 result
= 31 * result
+ b
;
1739 * Helper function to only assign the value if it is not already the default.
1741 private static void setIndexed(boolean indexed
, EntityV4
.Value
.Builder builder
) {
1742 if (indexed
!= builder
.getIndexed()) {
1743 builder
.setIndexed(indexed
);
1748 * Helper function to only assign the value if it is not already the default.
1750 private static void setMeaning(int meaning
, EntityV4
.Value
.Builder builder
) {
1751 if (meaning
!= builder
.getMeaning()) {
1752 builder
.setMeaning(meaning
);
1756 private DataTypeTranslator() {