Revision created by MOE tool push_codebase.
[gae.git] / java / src / main / com / google / appengine / api / datastore / DataTypeTranslator.java
blob5806947c05f9f3fc02942e335243cbd3de4bda75
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;
30 import java.util.Map;
32 /**
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();
41 /**
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();
52 static {
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();
89 /**
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.
94 private static final
95 Map<Class<? extends Comparable<?>>, Integer> comparableTypeMap =
96 new HashMap<Class<? extends Comparable<?>>, Integer>();
98 static {
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);
132 } else {
133 for (Object listValue : values) {
134 addPropertyToPb(name, listValue, indexed, true, proto);
137 } else {
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();
159 if (value != null) {
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);
167 if (!indexed) {
168 entity.addRawProperty(property);
169 } else {
170 entity.addProperty(property);
174 static PropertyValue toV3Value(Object value) {
175 PropertyValue propertyValue = new PropertyValue();
176 if (value != null) {
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);
227 return keyProp;
231 * Locates and returns all indexed properties with the given name on the
232 * given proto.
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()) {
262 return prop;
263 } else {
264 matchingMultipleProps.add(prop);
268 return null;
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));
283 results.add(value);
284 } else {
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);
302 return null;
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) {
326 if (indexOnly) {
327 for (EntityV4.PropertyOrBuilder prop : proto.getPropertyOrBuilderList()) {
328 map.put(prop.getName(), new RawValue(prop.getValue()));
330 } else {
331 for (EntityV4.PropertyOrBuilder prop : proto.getPropertyOrBuilderList()) {
332 addPropertyToMap(prop, map);
337 private static EntityV4.Value.Builder toV4Value(Object value, boolean indexed) {
338 if (value == null) {
339 EntityV4.Value.Builder builder = EntityV4.Value.newBuilder();
340 setIndexed(indexed, builder);
341 return 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));
355 } else {
356 EntityV4.Value.Builder valueBuilder = builder.getValueBuilder();
357 for (Object listValue : values) {
358 valueBuilder.addListValue(toV4Value(listValue, indexed));
361 } else {
362 builder.setValue(toV4Value(value, indexed));
364 return builder;
367 private static void addPropertyToMap(EntityV4.PropertyOrBuilder prop, Map<String, Object> map) {
368 EntityV4.ValueOrBuilder value = prop.getValueOrBuilder();
369 boolean indexed = value.getIndexed();
370 Object result;
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));
380 result = resultList;
381 } else {
382 result = getValue(value);
385 if (!indexed) {
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);
397 return null;
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());
411 return builder;
414 static EntityV4.Key.Builder toV4Key(Key key) {
415 EntityV4.Key.Builder builder = EntityV4.Key.newBuilder();
416 builder.setPartitionId(toV4PartitionId(key.getAppIdNamespace()));
417 do {
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);
427 return builder;
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.");
435 Key key = null;
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);
443 return key;
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
483 * handle.
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);
490 } else {
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> {
590 @Override
591 public boolean toV3Value(Object value, PropertyValue propertyValue) {
592 propertyValue.setStringValue(toDatastoreValue(value));
593 return true;
596 @Override
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);
602 return builder;
605 @Override
606 public final T getValue(PropertyValue propertyValue) {
607 return fromDatastoreValue(propertyValue.getStringValue());
610 @Override
611 public T getValue(EntityV4.ValueOrBuilder propertyValue) {
612 return fromDatastoreValue(propertyValue.getStringValue());
615 @Override
616 public final boolean hasValue(PropertyValue propertyValue) {
617 return propertyValue.hasStringValue();
620 @Override
621 public boolean hasValue(EntityV4.ValueOrBuilder propertyValue) {
622 return propertyValue.hasStringValue();
625 @Override
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();
639 @Override
640 public final boolean hasValue(PropertyValue propertyValue) {
641 return propertyValue.hasStringValue();
644 @Override
645 public final boolean hasValue(EntityV4.ValueOrBuilder propertyValue) {
646 return propertyValue.hasBlobValue();
649 @Override
650 public final boolean toV3Value(Object value, PropertyValue propertyValue) {
651 propertyValue.setStringValueAsBytes(toDatastoreValue(value));
652 return isIndexable();
655 @Override
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);
660 return builder;
663 @Override
664 public final T getValue(PropertyValue propertyValue) {
665 return fromDatastoreValue(propertyValue.getStringValueAsBytes());
668 @Override
669 public final T getValue(EntityV4.ValueOrBuilder propertyValue) {
670 return fromDatastoreValue(propertyValue.getBlobValue().toByteArray());
673 @Override
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);
694 @Override
695 public final boolean isType(EntityV4.ValueOrBuilder propertyValue) {
696 return propertyValue.getMeaning() == getV4Meaning() && hasValue(propertyValue);
699 @Override
700 public final boolean hasValue(EntityV4.ValueOrBuilder propertyValue) {
701 return propertyValue.hasEntityValue();
704 @Override
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);
710 return 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()
718 .setName(name)
719 .setValue(EntityV4.Value.newBuilder().setDoubleValue(value).setIndexed(false))
720 .build();
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()
728 .setName(name)
729 .setValue(EntityV4.Value.newBuilder().setStringValue(value).setIndexed(false))
730 .build();
733 @SuppressWarnings("unchecked")
734 @Override
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> {
746 @Override
747 public final boolean toV3Value(Object value, PropertyValue propertyValue) {
748 propertyValue.setInt64Value(toDatastoreValue(value));
749 return true;
752 @Override
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);
758 return builder;
761 @Override
762 public T getValue(PropertyValue propertyValue) {
763 return fromDatastoreValue(propertyValue.getInt64Value());
766 @Override
767 public T getValue(EntityV4.ValueOrBuilder propertyValue) {
768 return fromDatastoreValue(propertyValue.getIntegerValue());
771 @Override
772 public boolean hasValue(PropertyValue propertyValue) {
773 return propertyValue.hasInt64Value();
776 @Override
777 public boolean hasValue(EntityV4.ValueOrBuilder propertyValue) {
778 return propertyValue.hasIntegerValue();
781 @Override
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> {
791 @Override
792 public Meaning getV3Meaning() {
793 return Meaning.INDEX_VALUE;
796 @Override
797 public boolean hasValue(PropertyValue propertyValue) {
798 return true;
801 @Override
802 public boolean hasValue(EntityV4.ValueOrBuilder propertyValue) {
803 return true;
806 @Override
807 public boolean toV3Value(Object value, PropertyValue propertyValue) {
808 throw new UnsupportedOperationException();
811 @Override
812 public EntityV4.Value.Builder toV4Value(Object value, boolean indexed) {
813 throw new UnsupportedOperationException();
816 @Override
817 public RawValue getValue(PropertyValue propertyValue) {
818 return new RawValue(propertyValue);
821 @Override
822 public RawValue getValue(EntityV4.ValueOrBuilder propertyValue) {
823 EntityV4.Value value = null;
824 if (propertyValue instanceof EntityV4.Value) {
825 value = (EntityV4.Value) propertyValue;
826 } else {
827 value = ((EntityV4.Value.Builder) propertyValue).build();
829 return new RawValue(value);
832 @SuppressWarnings("unchecked")
833 @Override
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> {
847 @Override
848 protected String toDatastoreValue(Object value) {
849 return value.toString();
852 @Override
853 protected String fromDatastoreValue(String datastoreValue) {
854 return datastoreValue;
859 * The raw int64 type.
861 private static final class Int64Type extends BaseInt64Type<Long> {
862 @Override
863 protected Long toDatastoreValue(Object value) {
864 return ((Number) value).longValue();
867 @Override
868 protected Long fromDatastoreValue(Long datastoreValue) {
869 return datastoreValue;
874 * The raw double type.
876 private static final class DoubleType extends Type<Double> {
877 @Override
878 public boolean toV3Value(Object value, PropertyValue propertyValue) {
879 propertyValue.setDoubleValue(((Number) value).doubleValue());
880 return true;
883 @Override
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);
888 return builder;
891 @Override
892 public Double getValue(PropertyValue propertyValue) {
893 return propertyValue.getDoubleValue();
896 @Override
897 public Double getValue(EntityV4.ValueOrBuilder propertyValue) {
898 return propertyValue.getDoubleValue();
901 @Override
902 public boolean hasValue(PropertyValue propertyValue) {
903 return propertyValue.hasDoubleValue();
906 @Override
907 public boolean hasValue(EntityV4.ValueOrBuilder propertyValue) {
908 return propertyValue.hasDoubleValue();
911 @Override
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> {
921 @Override
922 public boolean toV3Value(Object value, PropertyValue propertyValue) {
923 propertyValue.setBooleanValue((Boolean) value);
924 return true;
927 @Override
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);
932 return builder;
935 @Override
936 public Boolean getValue(PropertyValue propertyValue) {
937 return propertyValue.isBooleanValue();
940 @Override
941 public Boolean getValue(EntityV4.ValueOrBuilder propertyValue) {
942 return propertyValue.getBooleanValue();
945 @Override
946 public boolean hasValue(PropertyValue propertyValue) {
947 return propertyValue.hasBooleanValue();
950 @Override
951 public boolean hasValue(EntityV4.ValueOrBuilder propertyValue) {
952 return propertyValue.hasBooleanValue();
955 @Override
956 public Boolean asComparable(Object value) {
957 return (Boolean) value;
962 * The user type.
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";
972 @Override
973 public int getV4Meaning() {
974 return MEANING_PREDEFINED_ENTITY_USER;
977 @Override
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();
989 @Override
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);
1000 return true;
1003 @Override
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);
1010 @Override
1011 public User getValue(EntityV4.ValueOrBuilder propertyValue) {
1012 String email = "";
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);
1028 @Override
1029 public boolean hasValue(PropertyValue propertyValue) {
1030 return propertyValue.hasUserValue();
1035 * The GeoPt type.
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";
1043 @Override
1044 public Meaning getV3Meaning() {
1045 return Meaning.GEORSS_POINT;
1048 @Override
1049 protected int getV4Meaning() {
1050 return getV3Meaning().getValue();
1053 @Override
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);
1060 return true;
1063 @Override
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()))
1069 .build();
1072 @Override
1073 public GeoPt getValue(PropertyValue propertyValue) {
1074 PropertyValue.PointValue pv = propertyValue.getPointValue();
1075 return new GeoPt((float) pv.getX(), (float) pv.getY());
1078 @Override
1079 public GeoPt getValue(EntityV4.ValueOrBuilder propertyValue) {
1080 double x = 0;
1081 double y = 0;
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);
1093 @Override
1094 public boolean hasValue(PropertyValue propertyValue) {
1095 return propertyValue.hasPointValue();
1100 * The key/reference type.
1102 private static final class KeyType extends Type<Key> {
1103 @Override
1104 public boolean toV3Value(Object value, PropertyValue propertyValue) {
1105 Reference keyRef = KeyTranslator.convertToPb((Key) value);
1106 propertyValue.setReferenceValue(toReferenceValue(keyRef));
1107 return true;
1110 @Override
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);
1115 return builder;
1118 @Override
1119 public Key getValue(PropertyValue propertyValue) {
1120 return KeyTranslator.createFromPb(toReference(propertyValue.getReferenceValue()));
1123 @Override
1124 public Key getValue(EntityV4.ValueOrBuilder propertyValue) {
1125 return toKey(propertyValue.getKeyValue());
1128 @Override
1129 public boolean hasValue(PropertyValue propertyValue) {
1130 return propertyValue.hasReferenceValue();
1133 @Override
1134 public boolean hasValue(EntityV4.ValueOrBuilder propertyValue) {
1135 return propertyValue.hasKeyValue();
1138 @Override
1139 public Key asComparable(Object value) {
1140 return (Key) 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);
1162 return refValue;
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);
1184 return reference;
1189 * The non-indexable blob type.
1191 private static class BlobType extends BaseBlobType<Blob> {
1192 @Override
1193 public Meaning getV3Meaning() {
1194 return Meaning.BLOB;
1197 @Override
1198 public boolean isType(EntityV4.ValueOrBuilder propertyValue) {
1199 return propertyValue.getMeaning() == 0
1200 && propertyValue.getIndexed() == false
1201 && hasValue(propertyValue);
1204 @Override
1205 protected Blob fromDatastoreValue(byte[] datastoreValue) {
1206 return new Blob(datastoreValue);
1209 @Override
1210 protected byte[] toDatastoreValue(Object value) {
1211 return ((Blob) value).getBytes();
1214 @Override
1215 public boolean isIndexable() {
1216 return false;
1221 * The indexable blob type.
1223 private static class ShortBlobType extends BaseBlobType<ShortBlob> {
1224 @Override
1225 public Meaning getV3Meaning() {
1226 return Meaning.BYTESTRING;
1229 @Override
1230 public boolean isType(EntityV4.ValueOrBuilder propertyValue) {
1231 if (!hasValue(propertyValue)) {
1232 return false;
1235 if (propertyValue.getIndexed()) {
1236 return propertyValue.getMeaning() == 0;
1237 } else {
1238 return propertyValue.getMeaning() == getV3Meaning().getValue();
1242 @Override
1243 public EntityV4.Value.Builder toV4Value(Object value, boolean indexed) {
1244 EntityV4.Value.Builder builder = super.toV4Value(value, indexed);
1245 if (!indexed) {
1246 builder.setMeaning(getV3Meaning().getValue());
1248 return builder;
1251 @Override
1252 protected byte[] toDatastoreValue(Object value) {
1253 return ((ShortBlob) value).getBytes();
1256 @Override
1257 protected ShortBlob fromDatastoreValue(byte[] datastoreValue) {
1258 return new ShortBlob(datastoreValue);
1261 @Override
1262 public boolean isIndexable() {
1263 return true;
1268 * The entity type.
1270 * Stored as a partially serialized EntityProto in V3.
1272 private static final class EmbeddedEntityType extends Type<EmbeddedEntity> {
1273 @Override
1274 public Meaning getV3Meaning() {
1275 return Meaning.ENTITY_PROTO;
1278 @Override
1279 public boolean isType(EntityV4.ValueOrBuilder propertyValue) {
1280 return propertyValue.getMeaning() == 0 && hasValue(propertyValue);
1283 @Override
1284 public boolean hasValue(PropertyValue propertyValue) {
1285 return propertyValue.hasStringValue();
1288 @Override
1289 public boolean hasValue(EntityV4.ValueOrBuilder propertyValue) {
1290 return propertyValue.hasEntityValue();
1293 @Override
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());
1302 return result;
1305 @Override
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());
1313 return result;
1316 @Override
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());
1325 return false;
1328 @Override
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);
1338 return builder;
1341 @Override
1342 public Comparable<?> asComparable(Object value) {
1343 return null;
1348 * The non-indexable {@link Text} type.
1350 private static final class TextType extends BaseStringType<Text> {
1351 @Override
1352 public Meaning getV3Meaning() {
1353 return Meaning.TEXT;
1356 @Override
1357 public boolean toV3Value(Object value, PropertyValue propertyValue) {
1358 super.toV3Value(value, propertyValue);
1359 return false;
1362 @Override
1363 public EntityV4.Value.Builder toV4Value(Object value, boolean indexed) {
1364 return super.toV4Value(value, false);
1367 @Override
1368 protected Text fromDatastoreValue(String datastoreString) {
1369 return new Text(datastoreString);
1372 @Override
1373 protected String toDatastoreValue(Object value) {
1374 return ((Text) value).getValue();
1377 @Override
1378 public ComparableByteArray asComparable(Object value) {
1379 return null;
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> {
1389 @Override
1390 public Meaning getV3Meaning() {
1391 return Meaning.BLOBKEY;
1394 @Override
1395 public boolean isType(EntityV4.ValueOrBuilder propertyValue) {
1396 return propertyValue.getMeaning() == 0 && hasValue(propertyValue);
1399 @Override
1400 public boolean hasValue(EntityV4.ValueOrBuilder propertyValue) {
1401 return propertyValue.hasBlobKeyValue();
1404 @Override
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);
1409 return builder;
1412 @Override
1413 public BlobKey getValue(EntityV4.ValueOrBuilder propertyValue) {
1414 return fromDatastoreValue(propertyValue.getBlobKeyValue());
1417 @Override
1418 protected String toDatastoreValue(Object value) {
1419 return ((BlobKey) value).getKeyString();
1422 @Override
1423 protected BlobKey fromDatastoreValue(String datastoreString) {
1424 return new BlobKey(datastoreString);
1429 * The date type.
1431 * In V3 dates are just int64s with a special meaning.
1433 private static final class DateType extends BaseInt64Type<Date> {
1434 @Override
1435 public Meaning getV3Meaning() {
1436 return Meaning.GD_WHEN;
1439 @Override
1440 public boolean isType(EntityV4.ValueOrBuilder propertyValue) {
1441 return propertyValue.getMeaning() == 0 && hasValue(propertyValue);
1444 @Override
1445 public boolean hasValue(EntityV4.ValueOrBuilder propertyValue) {
1446 return propertyValue.hasTimestampMicrosecondsValue();
1449 @Override
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);
1454 return builder;
1457 @Override
1458 public Date getValue(EntityV4.ValueOrBuilder propertyValue) {
1459 return fromDatastoreValue(propertyValue.getTimestampMicrosecondsValue());
1462 @Override
1463 protected Long toDatastoreValue(Object value) {
1464 return ((Date) value).getTime() * 1000L;
1467 @Override
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> {
1477 @Override
1478 public Meaning getV3Meaning() {
1479 return Meaning.ATOM_LINK;
1482 @Override
1483 protected String toDatastoreValue(Object value) {
1484 return ((Link) value).getValue();
1487 @Override
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> {
1497 @Override
1498 public Meaning getV3Meaning() {
1499 return Meaning.ATOM_CATEGORY;
1502 @Override
1503 protected String toDatastoreValue(Object value) {
1504 return ((Category) value).getCategory();
1507 @Override
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> {
1517 @Override
1518 public Meaning getV3Meaning() {
1519 return Meaning.GD_RATING;
1522 @Override
1523 protected Long toDatastoreValue(Object value) {
1524 return (long) ((Rating) value).getRating();
1527 @Override
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> {
1537 @Override
1538 public Meaning getV3Meaning() {
1539 return Meaning.GD_EMAIL;
1542 @Override
1543 protected String toDatastoreValue(Object value) {
1544 return ((Email) value).getEmail();
1547 @Override
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> {
1557 @Override
1558 public Meaning getV3Meaning() {
1559 return Meaning.GD_POSTALADDRESS;
1562 @Override
1563 protected String toDatastoreValue(Object value) {
1564 return ((PostalAddress) value).getAddress();
1567 @Override
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> {
1577 @Override
1578 public Meaning getV3Meaning() {
1579 return Meaning.GD_PHONENUMBER;
1582 @Override
1583 protected String toDatastoreValue(Object value) {
1584 return ((PhoneNumber) value).getNumber();
1587 @Override
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> {
1598 @Override
1599 public Meaning getV3Meaning() {
1600 return Meaning.GD_IM;
1603 @Override
1604 protected String toDatastoreValue(Object value) {
1605 return ((IMHandle) value).toDatastoreString();
1608 @Override
1609 protected IMHandle fromDatastoreValue(String datastoreString) {
1610 return IMHandle.fromDatastoreString(datastoreString);
1614 static Map<Class<?>, Type<?>> getTypeMap() {
1615 return typeMap;
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) {
1626 this.bytes = bytes;
1629 @Override
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;
1635 if (v1 != v2) {
1636 return v1 - v2;
1639 return bytes.length - otherBytes.length;
1642 @Override
1643 public boolean equals(Object obj) {
1644 if (obj == null) {
1645 return false;
1647 return Arrays.equals(bytes, ((ComparableByteArray) obj).bytes);
1650 @Override
1651 public int hashCode() {
1652 int result = 1;
1653 for (byte b : bytes) {
1654 result = 31 * result + b;
1656 return result;
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() {