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
.Maps
;
10 import com
.google
.io
.protocol
.ProtocolSupport
;
11 import com
.google
.protobuf
.ByteString
;
12 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.EntityProto
;
13 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.Path
;
14 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.Path
.Element
;
15 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.Property
;
16 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.Property
.Meaning
;
17 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.PropertyValue
;
18 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.PropertyValue
.ReferenceValue
;
19 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.PropertyValue
.ReferenceValuePathElement
;
20 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.PropertyValue
.UserValue
;
21 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.Reference
;
23 import java
.util
.ArrayList
;
24 import java
.util
.Arrays
;
25 import java
.util
.Collection
;
26 import java
.util
.Collections
;
27 import java
.util
.Date
;
28 import java
.util
.HashMap
;
29 import java
.util
.List
;
33 * {@code DataTypeTranslator} is a utility class for converting
34 * between the data store's {@code Property} protocol buffers and the
35 * user-facing classes ({@code String}, {@code User}, etc.).
38 public final class DataTypeTranslator
{
40 private static final RawValueType RAW_VALUE_TYPE
= new RawValueType();
42 * The list of supported types.
44 * Note: If you're going to modify this list, also update
45 * DataTypeUtils. We're not building {@link DataTypeUtils#getSupportedTypes}
46 * directly from this typeMap, because we want {@link DataTypeUtils} to be
47 * translatable by GWT, so that {@link Entity Entities} can be easily sent
48 * via GWT RPC. Also, if you add a type here that is not immutable you'll
49 * need to add special handling for it in {@link Entity#clone()}.
51 private static final Map
<Class
<?
>, Type
<?
>> typeMap
= Maps
.newHashMap();
53 typeMap
.put(RawValue
.class, RAW_VALUE_TYPE
);
55 typeMap
.put(Float
.class, new DoubleType());
56 typeMap
.put(Double
.class, new DoubleType());
58 typeMap
.put(Byte
.class, new Int64Type());
59 typeMap
.put(Short
.class, new Int64Type());
60 typeMap
.put(Integer
.class, new Int64Type());
61 typeMap
.put(Long
.class, new Int64Type());
62 typeMap
.put(Date
.class, new DateType());
63 typeMap
.put(Rating
.class, new RatingType());
65 typeMap
.put(String
.class, new StringType());
66 typeMap
.put(Link
.class, new LinkType());
67 typeMap
.put(ShortBlob
.class, new ShortBlobType());
68 typeMap
.put(Category
.class, new CategoryType());
69 typeMap
.put(PhoneNumber
.class, new PhoneNumberType());
70 typeMap
.put(PostalAddress
.class, new PostalAddressType());
71 typeMap
.put(Email
.class, new EmailType());
72 typeMap
.put(IMHandle
.class, new IMHandleType());
73 typeMap
.put(BlobKey
.class, new BlobKeyType());
74 typeMap
.put(Blob
.class, new BlobType());
75 typeMap
.put(Text
.class, new TextType());
76 typeMap
.put(EmbeddedEntity
.class, new EmbeddedEntityType());
78 typeMap
.put(Boolean
.class, new BoolType());
79 typeMap
.put(User
.class, new UserType());
80 typeMap
.put(Key
.class, new KeyType());
81 typeMap
.put(GeoPt
.class, new GeoPtType());
83 assert typeMap
.keySet().equals(DataTypeUtils
.getSupportedTypes()) :
84 "Warning: DataTypeUtils and DataTypeTranslator do not agree " +
85 "about supported classes: " + typeMap
.keySet() + " vs. " +
86 DataTypeUtils
.getSupportedTypes();
90 * A map with the {@link Comparable} classes returned by all the instances of
91 * {@link Type#asComparable(Object)} as keys and the pb code point as the value.
92 * Used for comparing values that don't map to the same pb code point.
95 Map
<Class
<?
extends Comparable
<?
>>, Integer
> comparableTypeMap
=
96 new HashMap
<Class
<?
extends Comparable
<?
>>, Integer
>();
99 comparableTypeMap
.put(ComparableByteArray
.class, PropertyValue
.kstringValue
);
100 comparableTypeMap
.put(Long
.class, PropertyValue
.kint64Value
);
101 comparableTypeMap
.put(Double
.class, PropertyValue
.kdoubleValue
);
102 comparableTypeMap
.put(Boolean
.class, PropertyValue
.kbooleanValue
);
103 comparableTypeMap
.put(User
.class, PropertyValue
.kUserValueGroup
);
104 comparableTypeMap
.put(Key
.class, PropertyValue
.kReferenceValueGroup
);
105 comparableTypeMap
.put(GeoPt
.class, PropertyValue
.kPointValueGroup
);
109 * Add all of the properties in the specified map to an {@code EntityProto}.
110 * This involves determining the type of each property and creating the
111 * proper type-specific protocol buffer.
113 * If the property value is an {@link UnindexedValue}, or if it's a
114 * type that is never indexed, e.g. {@code Text} and {@code Blob}, it's
115 * added to {@code EntityProto.raw_property}. Otherwise it's added to
116 * {@code EntityProto.property}.
118 * @param map A not {@code null} map of all the properties which will
119 * be set on {@code proto}
120 * @param proto A not {@code null} protocol buffer
122 public static void addPropertiesToPb(Map
<String
, Object
> map
, EntityProto proto
) {
123 for (Map
.Entry
<String
, Object
> entry
: map
.entrySet()) {
124 String name
= entry
.getKey();
125 boolean indexed
= !(entry
.getValue() instanceof UnindexedValue
);
126 Object value
= PropertyContainer
.unwrapValue(entry
.getValue());
128 if (value
instanceof Collection
<?
>) {
129 Collection
<?
> values
= (Collection
<?
>) value
;
130 if (values
.isEmpty()) {
131 addPropertyToPb(name
, null, indexed
, false, proto
);
133 for (Object listValue
: values
) {
134 addPropertyToPb(name
, listValue
, indexed
, true, proto
);
138 addPropertyToPb(name
, value
, indexed
, false, proto
);
144 * Adds a property to {@code entity}.
146 * @param name the property name
147 * @param value the property value
148 * @param indexed whether this property should be indexed. This may be
149 * overriden by property types like Blob and Text that are never indexed.
150 * @param multiple whether this property has multiple values
151 * @param entity the entity to populate
153 private static void addPropertyToPb(String name
, Object value
, boolean indexed
, boolean multiple
,
154 EntityProto entity
) {
155 Property property
= new Property();
156 property
.setName(name
);
157 property
.setMultiple(multiple
);
158 PropertyValue newValue
= property
.getMutableValue();
160 Type
<?
> type
= getType(value
.getClass());
161 Meaning meaning
= type
.getV3Meaning();
162 if (meaning
!= property
.getMeaningEnum()) {
163 property
.setMeaning(meaning
);
165 indexed
&= type
.toV3Value(value
, newValue
);
168 entity
.addRawProperty(property
);
170 entity
.addProperty(property
);
174 static PropertyValue
toV3Value(Object value
) {
175 PropertyValue propertyValue
= new PropertyValue();
177 getType(value
.getClass()).toV3Value(value
, propertyValue
);
179 return propertyValue
;
183 * Copy all of the indexed properties present on {@code proto} into {@code map}.
185 public static void extractIndexedPropertiesFromPb(EntityProto proto
, Map
<String
, Object
> map
) {
186 for (Property property
: proto
.propertys()) {
187 addPropertyToMap(property
, true, map
);
192 * Copy all of the unindexed properties present on {@code proto} into {@code map}.
194 private static void extractUnindexedPropertiesFromPb(EntityProto proto
, Map
<String
, Object
> map
) {
195 for (Property property
: proto
.rawPropertys()) {
196 addPropertyToMap(property
, false, map
);
201 * Copy all of the properties present on {@code proto} into {@code map}.
203 public static void extractPropertiesFromPb(EntityProto proto
, Map
<String
, Object
> map
) {
204 extractIndexedPropertiesFromPb(proto
, map
);
205 extractUnindexedPropertiesFromPb(proto
, map
);
209 * Copy all of the implicit properties present on {@code proto} into {@code map}.
211 public static void extractImplicitPropertiesFromPb(EntityProto proto
, Map
<String
, Object
> map
) {
212 for (Property property
: getImplicitProperties(proto
)) {
213 addPropertyToMap(property
, true, map
);
217 private static Iterable
<Property
> getImplicitProperties(EntityProto proto
) {
218 return Collections
.singleton(buildImplicitKeyProperty(proto
));
221 private static Property
buildImplicitKeyProperty(EntityProto proto
) {
222 Property keyProp
= new Property();
223 keyProp
.setName(Entity
.KEY_RESERVED_PROPERTY
);
224 PropertyValue propVal
= new PropertyValue();
225 propVal
.setReferenceValue(KeyType
.toReferenceValue(proto
.getKey()));
226 keyProp
.setValue(propVal
);
231 * Locates and returns all indexed properties with the given name on the
234 public static Collection
<Property
> findIndexedPropertiesOnPb(
235 EntityProto proto
, String propertyName
) {
236 if (propertyName
.equals(Entity
.KEY_RESERVED_PROPERTY
)) {
237 return Collections
.singleton(buildImplicitKeyProperty(proto
));
239 Collection
<Property
> multipleProps
= new ArrayList
<Property
>();
240 Property singleProp
= addPropertiesWithName(proto
.propertys(), propertyName
, multipleProps
);
241 if (singleProp
!= null) {
242 return Collections
.singleton(singleProp
);
244 return multipleProps
;
248 * Helper method that returns the first non-multiple property with the given name.
249 * If a multiple property with the given name is encountered it is added to the
250 * collection provided as an argument.
252 * @return A non-multiple property with the given name, or {@code null} if no
253 * such property was found or all properties found were multiple properties.
254 * If all properties found were multiple properties, the collection provided
255 * as an argument will contain all these properties.
257 private static Property
addPropertiesWithName(
258 Iterable
<Property
> props
, String propName
, Collection
<Property
> matchingMultipleProps
) {
259 for (Property prop
: props
) {
260 if (prop
.getName().equals(propName
)) {
261 if (!prop
.isMultiple()) {
264 matchingMultipleProps
.add(prop
);
271 private static void addPropertyToMap(Property property
, boolean indexed
,
272 Map
<String
, Object
> map
) {
273 String name
= property
.getName();
274 Object value
= getPropertyValue(property
);
276 if (property
.isMultiple()) {
277 @SuppressWarnings({"unchecked"})
278 List
<Object
> results
= (List
<Object
>) PropertyContainer
.unwrapValue(map
.get(name
));
279 if (results
== null) {
280 results
= new ArrayList
<Object
>();
281 map
.put(name
, indexed ? results
: new UnindexedValue(results
));
285 map
.put(name
, indexed ? value
: new UnindexedValue(value
));
290 * Returns the value for the property as its canonical type.
292 * @param property a not {@code null} property
293 * @return {@code null} if no value was set for {@code property}
295 public static Object
getPropertyValue(Property property
) {
296 PropertyValue value
= property
.getValue();
297 for (Type
<?
> type
: typeMap
.values()) {
298 if (type
.isType(property
.getMeaningEnum(), value
)) {
299 return type
.getValue(value
);
306 * @see #addPropertiesToPb(Map, EntityProto)
308 static void addPropertiesToPb(Map
<String
, Object
> map
, EntityV4
.Entity
.Builder proto
) {
309 for (Map
.Entry
<String
, Object
> entry
: map
.entrySet()) {
310 proto
.addProperty(toV4Property(entry
.getKey(), entry
.getValue()));
315 * Copy all of the properties present on {@code proto} into {@code map}.
317 * EntityV4 must know if the proto came from an index-only query as User and GeoPt types
318 * overwrite the INDEX_ONLY meaning.
320 * @param proto the proto from which to extract properties
321 * @param indexOnly if the proto is from an index only query (a projection query)
322 * @param map the map to populate
324 static void extractPropertiesFromPb(EntityV4
.EntityOrBuilder proto
, boolean indexOnly
,
325 Map
<String
, Object
> map
) {
327 for (EntityV4
.PropertyOrBuilder prop
: proto
.getPropertyOrBuilderList()) {
328 map
.put(prop
.getName(), new RawValue(prop
.getValue()));
331 for (EntityV4
.PropertyOrBuilder prop
: proto
.getPropertyOrBuilderList()) {
332 addPropertyToMap(prop
, map
);
337 private static EntityV4
.Value
.Builder
toV4Value(Object value
, boolean indexed
) {
339 EntityV4
.Value
.Builder builder
= EntityV4
.Value
.newBuilder();
340 setIndexed(indexed
, builder
);
343 return getType(value
.getClass()).toV4Value(value
, indexed
);
346 private static EntityV4
.Property
.Builder
toV4Property(String name
, Object value
) {
347 EntityV4
.Property
.Builder builder
= EntityV4
.Property
.newBuilder();
348 builder
.setName(name
);
349 boolean indexed
= !(value
instanceof UnindexedValue
);
350 value
= PropertyContainer
.unwrapValue(value
);
351 if (value
instanceof Collection
<?
>) {
352 Collection
<?
> values
= (Collection
<?
>) value
;
353 if (values
.isEmpty()) {
354 builder
.setValue(toV4Value(null, indexed
));
356 EntityV4
.Value
.Builder valueBuilder
= builder
.getValueBuilder();
357 for (Object listValue
: values
) {
358 valueBuilder
.addListValue(toV4Value(listValue
, indexed
));
362 builder
.setValue(toV4Value(value
, indexed
));
367 private static void addPropertyToMap(EntityV4
.PropertyOrBuilder prop
, Map
<String
, Object
> map
) {
368 EntityV4
.ValueOrBuilder value
= prop
.getValueOrBuilder();
369 boolean indexed
= value
.getIndexed();
371 if (value
.getListValueCount() > 0) {
372 ArrayList
<Object
> resultList
= new ArrayList
<Object
>(value
.getListValueCount());
373 for (EntityV4
.ValueOrBuilder subValue
: value
.getListValueOrBuilderList()) {
374 indexed
&= subValue
.getIndexed();
375 if (subValue
.getListValueCount() > 0) {
376 throw new IllegalArgumentException("Invalid Entity PB: list within a list.");
378 resultList
.add(getValue(subValue
));
382 result
= getValue(value
);
386 result
= new UnindexedValue(result
);
388 map
.put(prop
.getName(), result
);
391 private static Object
getValue(EntityV4
.ValueOrBuilder value
) {
392 for (Type
<?
> type
: typeMap
.values()) {
393 if (type
.isType(value
)) {
394 return type
.getValue(value
);
400 private static AppIdNamespace
toAppIdNamespace(EntityV4
.PartitionIdOrBuilder partitionId
) {
401 return new AppIdNamespace(partitionId
.getDatasetId(),
402 partitionId
.hasNamespace() ? partitionId
.getNamespace() : "");
405 private static EntityV4
.PartitionId
.Builder
toV4PartitionId(AppIdNamespace appNs
) {
406 EntityV4
.PartitionId
.Builder builder
= EntityV4
.PartitionId
.newBuilder();
407 builder
.setDatasetId(appNs
.getAppId());
408 if (!appNs
.getNamespace().isEmpty()) {
409 builder
.setNamespace(appNs
.getNamespace());
414 static EntityV4
.Key
.Builder
toV4Key(Key key
) {
415 EntityV4
.Key
.Builder builder
= EntityV4
.Key
.newBuilder();
416 builder
.setPartitionId(toV4PartitionId(key
.getAppIdNamespace()));
418 EntityV4
.Key
.PathElement
.Builder pathElement
= builder
.addPathElementBuilder();
419 pathElement
.setKind(key
.getKind());
420 if (key
.getName() != null) {
421 pathElement
.setName(key
.getName());
422 } else if (key
.getId() != Key
.NOT_ASSIGNED
) {
423 pathElement
.setId(key
.getId());
425 key
= key
.getParent();
426 } while (key
!= null);
430 static Key
toKey(EntityV4
.KeyOrBuilder proto
) {
431 AppIdNamespace appIdNamespace
= toAppIdNamespace(proto
.getPartitionId());
432 if (proto
.getPathElementCount() == 0) {
433 throw new IllegalArgumentException("Invalid Key PB: no elements.");
436 for (EntityV4
.Key
.PathElementOrBuilder e
: proto
.getPathElementOrBuilderList()) {
437 String kind
= e
.getKind();
438 if (e
.hasName() && e
.hasId()) {
439 throw new IllegalArgumentException("Invalid Key PB: both id and name are set.");
441 key
= new Key(kind
, key
, e
.getId(), e
.hasName() ? e
.getName() : null, appIdNamespace
);
447 * Returns the value for the property as its comparable representation type.
449 * @param property a not {@code null} property
450 * @return {@code null} if no value was set for {@code property}
452 @SuppressWarnings("unchecked")
453 public static Comparable
<Object
> getComparablePropertyValue(Property property
) {
454 return (Comparable
<Object
>) RAW_VALUE_TYPE
.asComparable(new RawValue(property
.getValue()));
458 * Converts the given {@link Object} into a supported value then returns it as
459 * a comparable object so it can be compared to other data types.
461 * @param value any Object that can be converted into a supported DataType
462 * @return {@code null} if value is null
463 * @throws UnsupportedOperationException if value is not supported
465 @SuppressWarnings("unchecked")
466 static Comparable
<Object
> getComparablePropertyValue(Object value
) {
467 return value
== null ?
null :
468 (Comparable
<Object
>) getType(value
.getClass()).asComparable(value
);
472 * Get the rank of the given datastore type relative to other datastore
473 * types. Note that datastore types do not necessarily have unique ranks.
475 @SuppressWarnings({"unchecked", "rawtypes"})
476 public static int getTypeRank(Class
<?
extends Comparable
> datastoreType
) {
477 return comparableTypeMap
.get(datastoreType
);
481 * Gets the {@link Type} that knows how to translate objects of
482 * type {@code clazz} into protocol buffers that the data store can
484 * @throws UnsupportedOperationException if clazz is not supported
486 @SuppressWarnings("unchecked")
487 private static <T
> Type
<T
> getType(Class
<T
> clazz
) {
488 if (typeMap
.containsKey(clazz
)) {
489 return (Type
<T
>) typeMap
.get(clazz
);
491 throw new UnsupportedOperationException("Unsupported data type: " + clazz
.getName());
496 * {@code Type} is an abstract class that knows how to convert Java
497 * objects of one or more types into datastore representations.
499 * @param <T> The canonical Java class for this type.
501 abstract static class Type
<T
> {
503 * @returns {@code true} if the given meaning and property value matches this {@link Type}.
505 public final boolean isType(Meaning meaning
, PropertyValue propertyValue
) {
506 return meaning
== getV3Meaning() && hasValue(propertyValue
);
510 * @returns {@code true} if the given value matches this {@link Type}.
512 public boolean isType(EntityV4
.ValueOrBuilder propertyValue
) {
513 return propertyValue
.getMeaning() == getV3Meaning().getValue() && hasValue(propertyValue
);
517 * Returns the {@link Comparable} for the given value, or
518 * {@code null} if values of this type are not comparable.
520 public abstract Comparable
<?
> asComparable(Object value
);
523 * Sets the value of {@code propertyValue} to {@code value}.
524 * @returns if the value is indexable
526 public abstract boolean toV3Value(Object value
, PropertyValue propertyValue
);
528 * Returns a new V4 Value for the given parameters.
530 * @param value the Java native value to convert
531 * @param indexed the desired indexing, ignored for types that are not indexable
532 * @returns the EntityV4 representation of the given value and desired indexing
534 public abstract EntityV4
.Value
.Builder
toV4Value(Object value
, boolean indexed
);
537 * Returns the value of {@code propertyValue} as its canonical Java type.
539 * Use {@link #isType} first to determine if the property has a value of the given type.
541 * @param propertyValue a not {@code null} value representing this {@code Type}
542 * @return the canonical Java representation of {@code propertyValue}.
544 public abstract T
getValue(PropertyValue propertyValue
);
546 * @see Type#getValue(PropertyValue)
548 public abstract T
getValue(EntityV4
.ValueOrBuilder propertyValue
);
551 * @returns {@code true} if a value of this {@code Type} is set on the given propertyValue.
553 public abstract boolean hasValue(PropertyValue propertyValue
);
555 * @returns {@code true} if a value of this {@code Type} is set on the given propertyValue.
557 public abstract boolean hasValue(EntityV4
.ValueOrBuilder propertyValue
);
560 * @returns the {@link Meaning} for this {@link Type}
562 protected Meaning
getV3Meaning() {
563 return Meaning
.NO_MEANING
;
568 * A base class with common functions for types that have the same datastore representation.
570 * @param <S> the datastore type
571 * @param <T> the canonical Java class for this type
573 private abstract static class BaseVariantType
<S
, T
> extends Type
<T
> {
575 * @returns the datastore representation of the given value
577 protected abstract S
toDatastoreValue(Object value
);
579 * @returns the native representation of the given value
581 protected abstract T
fromDatastoreValue(S datastoreValue
);
585 * Base class for types that store strings in the datastore.
587 * @param <T> the canonical Java class for this type
589 private abstract static class BaseStringType
<T
> extends BaseVariantType
<String
, T
> {
591 public boolean toV3Value(Object value
, PropertyValue propertyValue
) {
592 propertyValue
.setStringValue(toDatastoreValue(value
));
597 public EntityV4
.Value
.Builder
toV4Value(Object value
, boolean indexed
) {
598 EntityV4
.Value
.Builder builder
= EntityV4
.Value
.newBuilder();
599 builder
.setStringValue(toDatastoreValue(value
));
600 setIndexed(indexed
, builder
);
601 setMeaning(getV3Meaning().getValue(), builder
);
606 public final T
getValue(PropertyValue propertyValue
) {
607 return fromDatastoreValue(propertyValue
.getStringValue());
611 public T
getValue(EntityV4
.ValueOrBuilder propertyValue
) {
612 return fromDatastoreValue(propertyValue
.getStringValue());
616 public final boolean hasValue(PropertyValue propertyValue
) {
617 return propertyValue
.hasStringValue();
621 public boolean hasValue(EntityV4
.ValueOrBuilder propertyValue
) {
622 return propertyValue
.hasStringValue();
626 public ComparableByteArray
asComparable(Object value
) {
627 return new ComparableByteArray(ProtocolSupport
.toBytesUtf8(toDatastoreValue(value
)));
632 * Base class for types that store bytes in the datastore.
634 * @param <T> the canonical Java class for this type
636 private abstract static class BaseBlobType
<T
> extends BaseVariantType
<byte[], T
> {
637 protected abstract boolean isIndexable();
640 public final boolean hasValue(PropertyValue propertyValue
) {
641 return propertyValue
.hasStringValue();
645 public final boolean hasValue(EntityV4
.ValueOrBuilder propertyValue
) {
646 return propertyValue
.hasBlobValue();
650 public final boolean toV3Value(Object value
, PropertyValue propertyValue
) {
651 propertyValue
.setStringValueAsBytes(toDatastoreValue(value
));
652 return isIndexable();
656 public EntityV4
.Value
.Builder
toV4Value(Object value
, boolean indexed
) {
657 EntityV4
.Value
.Builder builder
= EntityV4
.Value
.newBuilder();
658 builder
.setBlobValue(ByteString
.copyFrom(toDatastoreValue(value
)));
659 setIndexed(indexed
&& isIndexable(), builder
);
664 public final T
getValue(PropertyValue propertyValue
) {
665 return fromDatastoreValue(propertyValue
.getStringValueAsBytes());
669 public final T
getValue(EntityV4
.ValueOrBuilder propertyValue
) {
670 return fromDatastoreValue(propertyValue
.getBlobValue().toByteArray());
674 public final ComparableByteArray
asComparable(Object value
) {
675 return isIndexable() ?
new ComparableByteArray(toDatastoreValue(value
)) : null;
680 * Base class for types that store predefined entities in V4.
682 * @param <T> the canonical Java class for this type
684 private abstract static class BasePredefinedEntityType
<T
> extends Type
<T
> {
686 * @returns the predefined entity meaning to use in V4
688 protected abstract int getV4Meaning();
690 * @returns the V4 Entity representation for the given value
692 protected abstract EntityV4
.Entity
getEntity(Object value
);
695 public final boolean isType(EntityV4
.ValueOrBuilder propertyValue
) {
696 return propertyValue
.getMeaning() == getV4Meaning() && hasValue(propertyValue
);
700 public final boolean hasValue(EntityV4
.ValueOrBuilder propertyValue
) {
701 return propertyValue
.hasEntityValue();
705 public final EntityV4
.Value
.Builder
toV4Value(Object value
, boolean indexed
) {
706 EntityV4
.Value
.Builder builder
= EntityV4
.Value
.newBuilder();
707 builder
.setEntityValue(getEntity(value
));
708 setIndexed(indexed
, builder
);
709 setMeaning(getV4Meaning(), builder
);
714 * @returns the V4 property representation for the given name and value, unindexed
716 protected EntityV4
.Property
makeUnindexedProperty(String name
, double value
) {
717 return EntityV4
.Property
.newBuilder()
719 .setValue(EntityV4
.Value
.newBuilder().setDoubleValue(value
).setIndexed(false))
724 * @returns the V4 property representation for the given name and value, unindexed
726 protected EntityV4
.Property
makeUnindexedProperty(String name
, String value
) {
727 return EntityV4
.Property
.newBuilder()
729 .setValue(EntityV4
.Value
.newBuilder().setStringValue(value
).setIndexed(false))
733 @SuppressWarnings("unchecked")
735 public final Comparable
<T
> asComparable(Object value
) {
736 return (Comparable
<T
>) value
;
741 * Base class for types that int64 values in the datastore.
743 * @param <T> the canonical Java class for this type
745 private abstract static class BaseInt64Type
<T
> extends BaseVariantType
<Long
, T
> {
747 public final boolean toV3Value(Object value
, PropertyValue propertyValue
) {
748 propertyValue
.setInt64Value(toDatastoreValue(value
));
753 public EntityV4
.Value
.Builder
toV4Value(Object value
, boolean indexed
) {
754 EntityV4
.Value
.Builder builder
= EntityV4
.Value
.newBuilder();
755 builder
.setIntegerValue(toDatastoreValue(value
));
756 setIndexed(indexed
, builder
);
757 setMeaning(getV3Meaning().getValue(), builder
);
762 public T
getValue(PropertyValue propertyValue
) {
763 return fromDatastoreValue(propertyValue
.getInt64Value());
767 public T
getValue(EntityV4
.ValueOrBuilder propertyValue
) {
768 return fromDatastoreValue(propertyValue
.getIntegerValue());
772 public boolean hasValue(PropertyValue propertyValue
) {
773 return propertyValue
.hasInt64Value();
777 public boolean hasValue(EntityV4
.ValueOrBuilder propertyValue
) {
778 return propertyValue
.hasIntegerValue();
782 public Long
asComparable(Object value
) {
783 return toDatastoreValue(value
);
788 * The type for projected index values.
790 private static final class RawValueType
extends Type
<RawValue
> {
792 public Meaning
getV3Meaning() {
793 return Meaning
.INDEX_VALUE
;
797 public boolean hasValue(PropertyValue propertyValue
) {
802 public boolean hasValue(EntityV4
.ValueOrBuilder propertyValue
) {
807 public boolean toV3Value(Object value
, PropertyValue propertyValue
) {
808 throw new UnsupportedOperationException();
812 public EntityV4
.Value
.Builder
toV4Value(Object value
, boolean indexed
) {
813 throw new UnsupportedOperationException();
817 public RawValue
getValue(PropertyValue propertyValue
) {
818 return new RawValue(propertyValue
);
822 public RawValue
getValue(EntityV4
.ValueOrBuilder propertyValue
) {
823 EntityV4
.Value value
= null;
824 if (propertyValue
instanceof EntityV4
.Value
) {
825 value
= (EntityV4
.Value
) propertyValue
;
827 value
= ((EntityV4
.Value
.Builder
) propertyValue
).build();
829 return new RawValue(value
);
832 @SuppressWarnings("unchecked")
834 public Comparable
<?
> asComparable(Object value
) {
835 value
= ((RawValue
) value
).getValue();
836 if (value
instanceof byte[]) {
837 return new ComparableByteArray((byte[]) value
);
839 return (Comparable
<?
>) value
;
844 * The raw String type.
846 private static final class StringType
extends BaseStringType
<String
> {
848 protected String
toDatastoreValue(Object value
) {
849 return value
.toString();
853 protected String
fromDatastoreValue(String datastoreValue
) {
854 return datastoreValue
;
859 * The raw int64 type.
861 private static final class Int64Type
extends BaseInt64Type
<Long
> {
863 protected Long
toDatastoreValue(Object value
) {
864 return ((Number
) value
).longValue();
868 protected Long
fromDatastoreValue(Long datastoreValue
) {
869 return datastoreValue
;
874 * The raw double type.
876 private static final class DoubleType
extends Type
<Double
> {
878 public boolean toV3Value(Object value
, PropertyValue propertyValue
) {
879 propertyValue
.setDoubleValue(((Number
) value
).doubleValue());
884 public EntityV4
.Value
.Builder
toV4Value(Object value
, boolean indexed
) {
885 EntityV4
.Value
.Builder builder
= EntityV4
.Value
.newBuilder();
886 builder
.setDoubleValue(((Number
) value
).doubleValue());
887 setIndexed(indexed
, builder
);
892 public Double
getValue(PropertyValue propertyValue
) {
893 return propertyValue
.getDoubleValue();
897 public Double
getValue(EntityV4
.ValueOrBuilder propertyValue
) {
898 return propertyValue
.getDoubleValue();
902 public boolean hasValue(PropertyValue propertyValue
) {
903 return propertyValue
.hasDoubleValue();
907 public boolean hasValue(EntityV4
.ValueOrBuilder propertyValue
) {
908 return propertyValue
.hasDoubleValue();
912 public Double
asComparable(Object value
) {
913 return ((Number
) value
).doubleValue();
918 * The raw boolean type.
920 private static final class BoolType
extends Type
<Boolean
> {
922 public boolean toV3Value(Object value
, PropertyValue propertyValue
) {
923 propertyValue
.setBooleanValue((Boolean
) value
);
928 public EntityV4
.Value
.Builder
toV4Value(Object value
, boolean indexed
) {
929 EntityV4
.Value
.Builder builder
= EntityV4
.Value
.newBuilder();
930 builder
.setBooleanValue((Boolean
) value
);
931 setIndexed(indexed
, builder
);
936 public Boolean
getValue(PropertyValue propertyValue
) {
937 return propertyValue
.isBooleanValue();
941 public Boolean
getValue(EntityV4
.ValueOrBuilder propertyValue
) {
942 return propertyValue
.getBooleanValue();
946 public boolean hasValue(PropertyValue propertyValue
) {
947 return propertyValue
.hasBooleanValue();
951 public boolean hasValue(EntityV4
.ValueOrBuilder propertyValue
) {
952 return propertyValue
.hasBooleanValue();
956 public Boolean
asComparable(Object value
) {
957 return (Boolean
) value
;
964 * Stored as an entity with a special meaning in V4.
966 private static final class UserType
extends BasePredefinedEntityType
<User
> {
967 public static final int MEANING_PREDEFINED_ENTITY_USER
= 20;
968 public static final String PROPERTY_NAME_EMAIL
= "email";
969 public static final String PROPERTY_NAME_AUTH_DOMAIN
= "auth_domain";
970 public static final String PROPERTY_NAME_USER_ID
= "user_id";
973 public int getV4Meaning() {
974 return MEANING_PREDEFINED_ENTITY_USER
;
978 public EntityV4
.Entity
getEntity(Object value
) {
979 User user
= (User
) value
;
980 EntityV4
.Entity
.Builder builder
= EntityV4
.Entity
.newBuilder();
981 builder
.addProperty(makeUnindexedProperty(PROPERTY_NAME_EMAIL
, user
.getEmail()));
982 builder
.addProperty(makeUnindexedProperty(PROPERTY_NAME_AUTH_DOMAIN
, user
.getAuthDomain()));
983 if (user
.getUserId() != null) {
984 builder
.addProperty(makeUnindexedProperty(PROPERTY_NAME_USER_ID
, user
.getUserId()));
986 return builder
.build();
990 public boolean toV3Value(Object value
, PropertyValue propertyValue
) {
991 User user
= (User
) value
;
992 UserValue userValue
= new UserValue();
993 userValue
.setEmail(user
.getEmail());
994 userValue
.setAuthDomain(user
.getAuthDomain());
995 if (user
.getUserId() != null) {
996 userValue
.setObfuscatedGaiaid(user
.getUserId());
998 userValue
.setGaiaid(0);
999 propertyValue
.setUserValue(userValue
);
1004 public User
getValue(PropertyValue propertyValue
) {
1005 UserValue userValue
= propertyValue
.getUserValue();
1006 String userId
= userValue
.hasObfuscatedGaiaid() ? userValue
.getObfuscatedGaiaid() : null;
1007 return new User(userValue
.getEmail(), userValue
.getAuthDomain(), userId
);
1011 public User
getValue(EntityV4
.ValueOrBuilder propertyValue
) {
1013 String authDomain
= "";
1014 String userId
= null;
1015 for (EntityV4
.PropertyOrBuilder prop
:
1016 propertyValue
.getEntityValueOrBuilder().getPropertyOrBuilderList()) {
1017 if (prop
.getName().equals(PROPERTY_NAME_EMAIL
)) {
1018 email
= prop
.getValueOrBuilder().getStringValue();
1019 } else if (prop
.getName().equals(PROPERTY_NAME_AUTH_DOMAIN
)) {
1020 authDomain
= prop
.getValueOrBuilder().getStringValue();
1021 } else if (prop
.getName().equals(PROPERTY_NAME_USER_ID
)) {
1022 userId
= prop
.getValueOrBuilder().getStringValue();
1025 return new User(email
, authDomain
, userId
);
1029 public boolean hasValue(PropertyValue propertyValue
) {
1030 return propertyValue
.hasUserValue();
1037 * Stored as an entity with a special meaning in V4.
1039 private static class GeoPtType
extends BasePredefinedEntityType
<GeoPt
> {
1040 public static final String PROPERTY_NAME_X
= "x";
1041 public static final String PROPERTY_NAME_Y
= "y";
1044 public Meaning
getV3Meaning() {
1045 return Meaning
.GEORSS_POINT
;
1049 protected int getV4Meaning() {
1050 return getV3Meaning().getValue();
1054 public boolean toV3Value(Object value
, PropertyValue propertyValue
) {
1055 GeoPt geoPt
= (GeoPt
) value
;
1056 PropertyValue
.PointValue pv
= new PropertyValue
.PointValue()
1057 .setX(geoPt
.getLatitude())
1058 .setY(geoPt
.getLongitude());
1059 propertyValue
.setPointValue(pv
);
1064 protected EntityV4
.Entity
getEntity(Object value
) {
1065 GeoPt geoPt
= (GeoPt
) value
;
1066 return EntityV4
.Entity
.newBuilder()
1067 .addProperty(makeUnindexedProperty(PROPERTY_NAME_X
, geoPt
.getLatitude()))
1068 .addProperty(makeUnindexedProperty(PROPERTY_NAME_Y
, geoPt
.getLongitude()))
1073 public GeoPt
getValue(PropertyValue propertyValue
) {
1074 PropertyValue
.PointValue pv
= propertyValue
.getPointValue();
1075 return new GeoPt((float) pv
.getX(), (float) pv
.getY());
1079 public GeoPt
getValue(EntityV4
.ValueOrBuilder propertyValue
) {
1082 for (EntityV4
.PropertyOrBuilder prop
:
1083 propertyValue
.getEntityValueOrBuilder().getPropertyOrBuilderList()) {
1084 if (prop
.getName().equals(PROPERTY_NAME_X
)) {
1085 x
= prop
.getValueOrBuilder().getDoubleValue();
1086 } else if (prop
.getName().equals(PROPERTY_NAME_Y
)) {
1087 y
= prop
.getValueOrBuilder().getDoubleValue();
1090 return new GeoPt((float) x
, (float) y
);
1094 public boolean hasValue(PropertyValue propertyValue
) {
1095 return propertyValue
.hasPointValue();
1100 * The key/reference type.
1102 private static final class KeyType
extends Type
<Key
> {
1104 public boolean toV3Value(Object value
, PropertyValue propertyValue
) {
1105 Reference keyRef
= KeyTranslator
.convertToPb((Key
) value
);
1106 propertyValue
.setReferenceValue(toReferenceValue(keyRef
));
1111 public EntityV4
.Value
.Builder
toV4Value(Object value
, boolean indexed
) {
1112 EntityV4
.Value
.Builder builder
= EntityV4
.Value
.newBuilder();
1113 builder
.setKeyValue(toV4Key((Key
) value
));
1114 setIndexed(indexed
, builder
);
1119 public Key
getValue(PropertyValue propertyValue
) {
1120 return KeyTranslator
.createFromPb(toReference(propertyValue
.getReferenceValue()));
1124 public Key
getValue(EntityV4
.ValueOrBuilder propertyValue
) {
1125 return toKey(propertyValue
.getKeyValue());
1129 public boolean hasValue(PropertyValue propertyValue
) {
1130 return propertyValue
.hasReferenceValue();
1134 public boolean hasValue(EntityV4
.ValueOrBuilder propertyValue
) {
1135 return propertyValue
.hasKeyValue();
1139 public Key
asComparable(Object value
) {
1143 private static ReferenceValue
toReferenceValue(Reference keyRef
) {
1144 ReferenceValue refValue
= new ReferenceValue();
1145 refValue
.setApp(keyRef
.getApp());
1146 if (keyRef
.hasNameSpace()) {
1147 refValue
.setNameSpace(keyRef
.getNameSpace());
1149 Path path
= keyRef
.getPath();
1150 for (Element element
: path
.elements()) {
1151 ReferenceValuePathElement newElement
= new ReferenceValuePathElement();
1152 newElement
.setType(element
.getType());
1153 if (element
.hasName()) {
1154 newElement
.setName(element
.getName());
1156 if (element
.hasId()) {
1157 newElement
.setId(element
.getId());
1159 refValue
.addPathElement(newElement
);
1165 private static Reference
toReference(ReferenceValue refValue
) {
1166 Reference reference
= new Reference();
1167 reference
.setApp(refValue
.getApp());
1168 if (refValue
.hasNameSpace()) {
1169 reference
.setNameSpace(refValue
.getNameSpace());
1171 Path path
= new Path();
1172 for (ReferenceValuePathElement element
: refValue
.pathElements()) {
1173 Element newElement
= new Element();
1174 newElement
.setType(element
.getType());
1175 if (element
.hasName()) {
1176 newElement
.setName(element
.getName());
1178 if (element
.hasId()) {
1179 newElement
.setId(element
.getId());
1181 path
.addElement(newElement
);
1183 reference
.setPath(path
);
1189 * The non-indexable blob type.
1191 private static class BlobType
extends BaseBlobType
<Blob
> {
1193 public Meaning
getV3Meaning() {
1194 return Meaning
.BLOB
;
1198 public boolean isType(EntityV4
.ValueOrBuilder propertyValue
) {
1199 return propertyValue
.getMeaning() == 0
1200 && propertyValue
.getIndexed() == false
1201 && hasValue(propertyValue
);
1205 protected Blob
fromDatastoreValue(byte[] datastoreValue
) {
1206 return new Blob(datastoreValue
);
1210 protected byte[] toDatastoreValue(Object value
) {
1211 return ((Blob
) value
).getBytes();
1215 public boolean isIndexable() {
1221 * The indexable blob type.
1223 private static class ShortBlobType
extends BaseBlobType
<ShortBlob
> {
1225 public Meaning
getV3Meaning() {
1226 return Meaning
.BYTESTRING
;
1230 public boolean isType(EntityV4
.ValueOrBuilder propertyValue
) {
1231 if (!hasValue(propertyValue
)) {
1235 if (propertyValue
.getIndexed()) {
1236 return propertyValue
.getMeaning() == 0;
1238 return propertyValue
.getMeaning() == getV3Meaning().getValue();
1243 public EntityV4
.Value
.Builder
toV4Value(Object value
, boolean indexed
) {
1244 EntityV4
.Value
.Builder builder
= super.toV4Value(value
, indexed
);
1246 builder
.setMeaning(getV3Meaning().getValue());
1252 protected byte[] toDatastoreValue(Object value
) {
1253 return ((ShortBlob
) value
).getBytes();
1257 protected ShortBlob
fromDatastoreValue(byte[] datastoreValue
) {
1258 return new ShortBlob(datastoreValue
);
1262 public boolean isIndexable() {
1270 * Stored as a partially serialized EntityProto in V3.
1272 private static final class EmbeddedEntityType
extends Type
<EmbeddedEntity
> {
1274 public Meaning
getV3Meaning() {
1275 return Meaning
.ENTITY_PROTO
;
1279 public boolean isType(EntityV4
.ValueOrBuilder propertyValue
) {
1280 return propertyValue
.getMeaning() == 0 && hasValue(propertyValue
);
1284 public boolean hasValue(PropertyValue propertyValue
) {
1285 return propertyValue
.hasStringValue();
1289 public boolean hasValue(EntityV4
.ValueOrBuilder propertyValue
) {
1290 return propertyValue
.hasEntityValue();
1294 public EmbeddedEntity
getValue(PropertyValue propertyValue
) {
1295 EntityProto proto
= new EntityProto();
1296 proto
.mergeFrom(propertyValue
.getStringValueAsBytes());
1297 EmbeddedEntity result
= new EmbeddedEntity();
1298 if (proto
.hasKey() && !proto
.getKey().getApp().isEmpty()) {
1299 result
.setKey(KeyTranslator
.createFromPb(proto
.getKey()));
1301 extractPropertiesFromPb(proto
, result
.getPropertyMap());
1306 public EmbeddedEntity
getValue(EntityV4
.ValueOrBuilder propertyValue
) {
1307 EmbeddedEntity result
= new EmbeddedEntity();
1308 EntityV4
.Entity proto
= propertyValue
.getEntityValue();
1309 if (proto
.hasKey()) {
1310 result
.setKey(toKey(proto
.getKey()));
1312 extractPropertiesFromPb(proto
, false, result
.getPropertyMap());
1317 public boolean toV3Value(Object value
, PropertyValue propertyValue
) {
1318 EmbeddedEntity structProp
= (EmbeddedEntity
) value
;
1319 EntityProto proto
= new EntityProto();
1320 if (structProp
.getKey() != null) {
1321 proto
.setKey(KeyTranslator
.convertToPb(structProp
.getKey()));
1323 addPropertiesToPb(structProp
.getPropertyMap(), proto
);
1324 propertyValue
.setStringValueAsBytes(proto
.toByteArray());
1329 public EntityV4
.Value
.Builder
toV4Value(Object value
, boolean indexed
) {
1330 EmbeddedEntity structProp
= (EmbeddedEntity
) value
;
1331 EntityV4
.Value
.Builder builder
= EntityV4
.Value
.newBuilder();
1332 EntityV4
.Entity
.Builder proto
= builder
.getEntityValueBuilder();
1333 if (structProp
.getKey() != null) {
1334 proto
.setKey(toV4Key(structProp
.getKey()));
1336 addPropertiesToPb(structProp
.getPropertyMap(), proto
);
1337 builder
.setIndexed(false);
1342 public Comparable
<?
> asComparable(Object value
) {
1348 * The non-indexable {@link Text} type.
1350 private static final class TextType
extends BaseStringType
<Text
> {
1352 public Meaning
getV3Meaning() {
1353 return Meaning
.TEXT
;
1357 public boolean toV3Value(Object value
, PropertyValue propertyValue
) {
1358 super.toV3Value(value
, propertyValue
);
1363 public EntityV4
.Value
.Builder
toV4Value(Object value
, boolean indexed
) {
1364 return super.toV4Value(value
, false);
1368 protected Text
fromDatastoreValue(String datastoreString
) {
1369 return new Text(datastoreString
);
1373 protected String
toDatastoreValue(Object value
) {
1374 return ((Text
) value
).getValue();
1378 public ComparableByteArray
asComparable(Object value
) {
1384 * The {@link BlobKey} type.
1386 * In V3 dates are just strings with a special meaning.
1388 private static final class BlobKeyType
extends BaseStringType
<BlobKey
> {
1390 public Meaning
getV3Meaning() {
1391 return Meaning
.BLOBKEY
;
1395 public boolean isType(EntityV4
.ValueOrBuilder propertyValue
) {
1396 return propertyValue
.getMeaning() == 0 && hasValue(propertyValue
);
1400 public boolean hasValue(EntityV4
.ValueOrBuilder propertyValue
) {
1401 return propertyValue
.hasBlobKeyValue();
1405 public EntityV4
.Value
.Builder
toV4Value(Object value
, boolean indexed
) {
1406 EntityV4
.Value
.Builder builder
= EntityV4
.Value
.newBuilder();
1407 builder
.setBlobKeyValue(toDatastoreValue(value
));
1408 setIndexed(indexed
, builder
);
1413 public BlobKey
getValue(EntityV4
.ValueOrBuilder propertyValue
) {
1414 return fromDatastoreValue(propertyValue
.getBlobKeyValue());
1418 protected String
toDatastoreValue(Object value
) {
1419 return ((BlobKey
) value
).getKeyString();
1423 protected BlobKey
fromDatastoreValue(String datastoreString
) {
1424 return new BlobKey(datastoreString
);
1431 * In V3 dates are just int64s with a special meaning.
1433 private static final class DateType
extends BaseInt64Type
<Date
> {
1435 public Meaning
getV3Meaning() {
1436 return Meaning
.GD_WHEN
;
1440 public boolean isType(EntityV4
.ValueOrBuilder propertyValue
) {
1441 return propertyValue
.getMeaning() == 0 && hasValue(propertyValue
);
1445 public boolean hasValue(EntityV4
.ValueOrBuilder propertyValue
) {
1446 return propertyValue
.hasTimestampMicrosecondsValue();
1450 public EntityV4
.Value
.Builder
toV4Value(Object value
, boolean indexed
) {
1451 EntityV4
.Value
.Builder builder
= EntityV4
.Value
.newBuilder();
1452 builder
.setTimestampMicrosecondsValue(toDatastoreValue(value
));
1453 setIndexed(indexed
, builder
);
1458 public Date
getValue(EntityV4
.ValueOrBuilder propertyValue
) {
1459 return fromDatastoreValue(propertyValue
.getTimestampMicrosecondsValue());
1463 protected Long
toDatastoreValue(Object value
) {
1464 return ((Date
) value
).getTime() * 1000L;
1468 protected Date
fromDatastoreValue(Long datastoreValue
) {
1469 return new Date(datastoreValue
/ 1000L);
1474 * Internally a link is just a string with a special meaning.
1476 private static final class LinkType
extends BaseStringType
<Link
> {
1478 public Meaning
getV3Meaning() {
1479 return Meaning
.ATOM_LINK
;
1483 protected String
toDatastoreValue(Object value
) {
1484 return ((Link
) value
).getValue();
1488 protected Link
fromDatastoreValue(String datastoreValue
) {
1489 return new Link(datastoreValue
);
1494 * Internally a category is just a string with a special meaning.
1496 private static final class CategoryType
extends BaseStringType
<Category
> {
1498 public Meaning
getV3Meaning() {
1499 return Meaning
.ATOM_CATEGORY
;
1503 protected String
toDatastoreValue(Object value
) {
1504 return ((Category
) value
).getCategory();
1508 protected Category
fromDatastoreValue(String datastoreString
) {
1509 return new Category(datastoreString
);
1514 * Internally a rating is just an int64 with a special meaning.
1516 private static final class RatingType
extends BaseInt64Type
<Rating
> {
1518 public Meaning
getV3Meaning() {
1519 return Meaning
.GD_RATING
;
1523 protected Long
toDatastoreValue(Object value
) {
1524 return (long) ((Rating
) value
).getRating();
1528 protected Rating
fromDatastoreValue(Long datastoreLong
) {
1529 return new Rating(datastoreLong
.intValue());
1534 * Internally an email is just a string with a special meaning.
1536 private static final class EmailType
extends BaseStringType
<Email
> {
1538 public Meaning
getV3Meaning() {
1539 return Meaning
.GD_EMAIL
;
1543 protected String
toDatastoreValue(Object value
) {
1544 return ((Email
) value
).getEmail();
1548 protected Email
fromDatastoreValue(String datastoreString
) {
1549 return new Email(datastoreString
);
1554 * Internally a postal address is just a string with a special meaning.
1556 private static final class PostalAddressType
extends BaseStringType
<PostalAddress
> {
1558 public Meaning
getV3Meaning() {
1559 return Meaning
.GD_POSTALADDRESS
;
1563 protected String
toDatastoreValue(Object value
) {
1564 return ((PostalAddress
) value
).getAddress();
1568 protected PostalAddress
fromDatastoreValue(String datastoreString
) {
1569 return new PostalAddress(datastoreString
);
1574 * Internally a phone number is just a string with a special meaning.
1576 private static final class PhoneNumberType
extends BaseStringType
<PhoneNumber
> {
1578 public Meaning
getV3Meaning() {
1579 return Meaning
.GD_PHONENUMBER
;
1583 protected String
toDatastoreValue(Object value
) {
1584 return ((PhoneNumber
) value
).getNumber();
1588 protected PhoneNumber
fromDatastoreValue(String datastoreString
) {
1589 return new PhoneNumber(datastoreString
);
1594 * Internally an IM handle is just a string with a special meaning and a
1595 * well known format.
1597 private static final class IMHandleType
extends BaseStringType
<IMHandle
> {
1599 public Meaning
getV3Meaning() {
1600 return Meaning
.GD_IM
;
1604 protected String
toDatastoreValue(Object value
) {
1605 return ((IMHandle
) value
).toDatastoreString();
1609 protected IMHandle
fromDatastoreValue(String datastoreString
) {
1610 return IMHandle
.fromDatastoreString(datastoreString
);
1614 static Map
<Class
<?
>, Type
<?
>> getTypeMap() {
1619 * A wrapper for a {@code byte[]} that implements {@link Comparable}.
1620 * Comparison algorithm is the same as the prod datastore.
1622 public static final class ComparableByteArray
implements Comparable
<ComparableByteArray
> {
1623 private final byte[] bytes
;
1625 public ComparableByteArray(byte[] bytes
) {
1630 public int compareTo(ComparableByteArray other
) {
1631 byte[] otherBytes
= other
.bytes
;
1632 for (int i
= 0; i
< Math
.min(bytes
.length
, otherBytes
.length
); i
++) {
1633 int v1
= bytes
[i
] & 0xFF;
1634 int v2
= otherBytes
[i
] & 0xFF;
1639 return bytes
.length
- otherBytes
.length
;
1643 public boolean equals(Object obj
) {
1647 return Arrays
.equals(bytes
, ((ComparableByteArray
) obj
).bytes
);
1651 public int hashCode() {
1653 for (byte b
: bytes
) {
1654 result
= 31 * result
+ b
;
1661 * Helper function to only assign the value if it is not already the default.
1663 private static void setIndexed(boolean indexed
, EntityV4
.Value
.Builder builder
) {
1664 if (indexed
!= builder
.getIndexed()) {
1665 builder
.setIndexed(indexed
);
1670 * Helper function to only assign the value if it is not already the default.
1672 private static void setMeaning(int meaning
, EntityV4
.Value
.Builder builder
) {
1673 if (meaning
!= builder
.getMeaning()) {
1674 builder
.setMeaning(meaning
);
1678 private DataTypeTranslator() {