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