Revision created by MOE tool push_codebase.
[gae.git] / java / src / main / com / google / appengine / api / datastore / DataTypeTranslator.java
blob9c3a55d1f6cb6ca263a0a1c265489baa7ed392aa
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.common.collect.Maps;
9 import com.google.storage.onestore.v3.OnestoreEntity.EntityProto;
10 import com.google.storage.onestore.v3.OnestoreEntity.Path;
11 import com.google.storage.onestore.v3.OnestoreEntity.Path.Element;
12 import com.google.storage.onestore.v3.OnestoreEntity.Property;
13 import com.google.storage.onestore.v3.OnestoreEntity.Property.Meaning;
14 import com.google.storage.onestore.v3.OnestoreEntity.PropertyValue;
15 import com.google.storage.onestore.v3.OnestoreEntity.PropertyValue.ReferenceValue;
16 import com.google.storage.onestore.v3.OnestoreEntity.PropertyValue.ReferenceValuePathElement;
17 import com.google.storage.onestore.v3.OnestoreEntity.PropertyValue.UserValue;
18 import com.google.storage.onestore.v3.OnestoreEntity.Reference;
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.Collection;
23 import java.util.Collections;
24 import java.util.Date;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
29 /**
30 * {@code DataTypeTranslator} is a utility class for converting
31 * between the data store's {@code Property} protocol buffers and the
32 * user-facing classes ({@code String}, {@code User}, etc.).
35 public final class DataTypeTranslator {
37 private static final StringType STRING_TYPE = new StringType();
39 /**
40 * The list of supported types.
42 * Note: If you're going to modify this list, also update
43 * DataTypeUtils. We're not building {@link DataTypeUtils#getSupportedTypes}
44 * directly from this typeMap, because we want {@link DataTypeUtils} to be
45 * translatable by GWT, so that {@link Entity Entities} can be easily sent
46 * via GWT RPC. Also, if you add a type here that is not immutable you'll
47 * need to add special handling for it in {@link Entity#clone()}.
49 private static final Map<Class<?>, Type<?>> typeMap = Maps.newHashMap();
50 static {
51 typeMap.put(RawValue.class, new RawValueType());
53 typeMap.put(Float.class, new DoubleType());
54 typeMap.put(Double.class, new DoubleType());
56 typeMap.put(Byte.class, new Int64Type());
57 typeMap.put(Short.class, new Int64Type());
58 typeMap.put(Integer.class, new Int64Type());
59 typeMap.put(Long.class, new Int64Type());
60 typeMap.put(Date.class, new DateType());
61 typeMap.put(Rating.class, new RatingType());
63 typeMap.put(String.class, STRING_TYPE);
64 typeMap.put(Link.class, new LinkType());
65 typeMap.put(ShortBlob.class, new ShortBlobType());
66 typeMap.put(Category.class, new CategoryType());
67 typeMap.put(PhoneNumber.class, new PhoneNumberType());
68 typeMap.put(PostalAddress.class, new PostalAddressType());
69 typeMap.put(Email.class, new EmailType());
70 typeMap.put(IMHandle.class, new IMHandleType());
71 typeMap.put(BlobKey.class, new BlobKeyType());
72 typeMap.put(Blob.class, new BlobType());
73 typeMap.put(Text.class, new TextType());
74 typeMap.put(EmbeddedEntity.class, new EmbeddedEntityType());
76 typeMap.put(Boolean.class, new BoolType());
77 typeMap.put(User.class, new UserType());
78 typeMap.put(Key.class, new ReferenceType());
79 typeMap.put(GeoPt.class, new GeoPtType());
81 assert typeMap.keySet().equals(DataTypeUtils.getSupportedTypes()) :
82 "Warning: DataTypeUtils and DataTypeTranslator do not agree " +
83 "about supported classes: " + typeMap.keySet() + " vs. " +
84 DataTypeUtils.getSupportedTypes();
87 /**
88 * A map with the {@link Comparable} classes returned by all the instances of
89 * {@link AsComparableFunction} as keys and the pb code point as the value.
90 * Used for comparing values that don't map to the same pb code point.
92 private static final
93 Map<Class<? extends Comparable<?>>, Integer> comparableTypeMap =
94 new HashMap<Class<? extends Comparable<?>>, Integer>();
96 static {
97 comparableTypeMap.put(ComparableByteArray.class, PropertyValue.kstringValue);
98 comparableTypeMap.put(Long.class, PropertyValue.kint64Value);
99 comparableTypeMap.put(Double.class, PropertyValue.kdoubleValue);
100 comparableTypeMap.put(Boolean.class, PropertyValue.kbooleanValue);
101 comparableTypeMap.put(User.class, PropertyValue.kUserValueGroup);
102 comparableTypeMap.put(Key.class, PropertyValue.kReferenceValueGroup);
103 comparableTypeMap.put(GeoPt.class, PropertyValue.kPointValueGroup);
107 * Add all of the properties in the specified map to an {@code EntityProto}.
108 * This involves determining the type of each property and creating the
109 * proper type-specific protocol buffer.
111 * If the property value is an {@link UnindexedValue}, or if it's a
112 * type that is never indexed, e.g. {@code Text} and {@code Blob}, it's
113 * added to {@code EntityProto.raw_property}. Otherwise it's added to
114 * {@code EntityProto.property}.
116 * @param map A not {@code null} map of all the properties which will
117 * be set on {@code proto}
118 * @param proto A not {@code null} protocol buffer
120 public static void addPropertiesToPb(Map<String, Object> map, EntityProto proto) {
121 for (Map.Entry<String, Object> entry : map.entrySet()) {
122 String key = entry.getKey();
123 boolean indexed = !(entry.getValue() instanceof UnindexedValue);
124 Object value = PropertyContainer.unwrapValue(entry.getValue());
126 if (value instanceof Collection<?>) {
127 Collection<?> values = (Collection<?>) value;
128 if (values.isEmpty()) {
129 addProperty(proto, key, null, indexed, false);
130 } else {
131 for (Object listValue : values) {
132 addProperty(proto, key, listValue, indexed, true);
135 } else {
136 addProperty(proto, key, value, indexed, false);
142 * Adds a property to {@code entity}.
144 * @param entity a not {@code null} {@code EntityProto}
145 * @param name the property name
146 * @param value the property value
147 * @param indexed whether this property should be indexed. This may be
148 * overriden by property types like Blob and Text that are never indexed.
149 * @param multiple whether this property has multiple values
151 private static void addProperty(EntityProto entity, String name, Object value,
152 boolean indexed, boolean multiple) {
153 Pair<Type<?>, Property> pair = createProperty(name, value, multiple);
154 Type<?> type = pair.first();
155 Property property = pair.second();
157 if (!indexed || (type != null && type.getComparableFunction() == NOT_COMPARABLE)) {
158 entity.addRawProperty(property);
159 } else {
160 entity.addProperty(property);
165 * Creates a new {@link Property} given its {@code name}, {@code value},
166 * and {@code multiplicity}.
168 * @param name The name used as a key
169 * @param value The value for the Property
170 * @param multiple true iff there are also other Properties with the same name
172 * @return a not {@code null} Pair<Type, Property>. The {@code Type} will be null
173 * iff {@code value} is null. {@code Property} will not be null.
175 private static Pair<Type<?>, Property> createProperty(String name, Object value,
176 boolean multiple) {
177 Property property = new Property();
178 property.setName(name);
179 property.setMultiple(multiple);
181 if (value == null) {
182 return Pair.of(null, property);
185 Pair<Type<?>, PropertyValue> newValue = createPropertyValue(value);
186 Type<?> type = newValue.first;
187 Meaning meaning = type.getMeaning();
188 if (meaning != Meaning.NO_MEANING) {
189 property.setMeaning(meaning);
191 property.setValue(newValue.second);
192 return new Pair<Type<?>, Property>(type, property);
195 private static Pair<Type<?>, PropertyValue> createPropertyValue(Object value) {
196 if (value == null) {
197 return Pair.of(null, null);
199 PropertyValue newValue = new PropertyValue();
200 Type<?> type = getType(value.getClass());
201 type.setPropertyValue(newValue, value);
202 return new Pair<Type<?>, PropertyValue>(type, newValue);
206 * Copy all of the indexed properties present on {@code proto}
207 * into {@code map}.
209 public static void extractIndexedPropertiesFromPb(EntityProto proto, Map<String, Object> map) {
210 for (Property property : proto.propertys()) {
211 addPropertyValueToMap(property, map, true);
216 * Copy all of the unindexed properties present on {@code proto}
217 * into {@code map}.
219 private static void extractUnindexedPropertiesFromPb(EntityProto proto, Map<String, Object> map) {
220 for (Property property : proto.rawPropertys()) {
221 addPropertyValueToMap(property, map, false);
226 * Copy all of the properties present on {@code proto}
227 * into {@code map}.
229 public static void extractPropertiesFromPb(EntityProto proto, Map<String, Object> map) {
230 extractIndexedPropertiesFromPb(proto, map);
231 extractUnindexedPropertiesFromPb(proto, map);
235 * Copy all of the implicit properties present on {@code proto}
236 * into {@code map}.
238 public static void extractImplicitPropertiesFromPb(EntityProto proto, Map<String, Object> map) {
239 for (Property property : getImplicitProperties(proto)) {
240 addPropertyValueToMap(property, map, true);
244 private static Iterable<Property> getImplicitProperties(EntityProto proto) {
245 return Collections.singleton(buildImplicitKeyProperty(proto));
248 private static Property buildImplicitKeyProperty(EntityProto proto) {
249 Property keyProp = new Property();
250 keyProp.setName(Entity.KEY_RESERVED_PROPERTY);
251 PropertyValue propVal = new PropertyValue();
252 ReferenceType.setPropertyValue(propVal, proto.getKey());
253 keyProp.setValue(propVal);
254 return keyProp;
258 * Locates and returns all indexed properties with the given name on the
259 * given proto.
261 public static Collection<Property> findIndexedPropertiesOnPb(
262 EntityProto proto, String propertyName) {
263 if (propertyName.equals(Entity.KEY_RESERVED_PROPERTY)) {
264 return Collections.singleton(buildImplicitKeyProperty(proto));
266 Collection<Property> multipleProps = new ArrayList<Property>();
267 Property singleProp = addPropertiesWithName(proto.propertys(), propertyName, multipleProps);
268 if (singleProp != null) {
269 return Collections.singleton(singleProp);
271 return multipleProps;
275 * Helper method that returns the first non-multiple property with the given name.
276 * If a multiple property with the given name is encountered it is added to the
277 * collection provided as an argument.
279 * @return A non-multiple property with the given name, or {@code null} if no
280 * such property was found or all properties found were multiple properties.
281 * If all properties found were multiple properties, the collection provided
282 * as an argument will contain all these properties.
284 private static Property addPropertiesWithName(
285 Iterable<Property> props, String propName, Collection<Property> matchingMultipleProps) {
286 for (Property prop : props) {
287 if (prop.getName().equals(propName)) {
288 if (!prop.isMultiple()) {
289 return prop;
290 } else {
291 matchingMultipleProps.add(prop);
295 return null;
298 private static void addPropertyValueToMap(
299 Property property, Map<String, Object> map, boolean indexed) {
300 String name = property.getName();
301 Object value = getPropertyValue(property);
303 if (property.isMultiple()) {
304 @SuppressWarnings({"unchecked"})
305 List<Object> results = (List<Object>) PropertyContainer.unwrapValue(map.get(name));
306 if (results == null) {
307 results = new ArrayList<Object>();
308 map.put(name, indexed ? results : new UnindexedValue(results));
310 results.add(value);
311 } else {
312 map.put(name, indexed ? value : new UnindexedValue(value));
317 * Returns the value for the property as its canonical type.
319 * @param property a not {@code null} property
320 * @return {@code null} if no value was set for {@code property}
322 public static Object getPropertyValue(Property property) {
323 PropertyValue value = property.getValue();
324 for (Type<?> type : typeMap.values()) {
325 if (type.hasPropertyValue(value) &&
326 type.getMeaning() == property.getMeaningEnum()) {
327 return type.getPropertyValue(value);
330 return null;
334 * Returns the value for the property as its comparable representation type.
336 * @param property a not {@code null} property
337 * @return {@code null} if no value was set for {@code property}
339 public static Comparable<Object> getComparablePropertyValue(Property property) {
340 PropertyValue value = property.getValue();
341 Meaning meaning = property.getMeaningEnum();
342 for (Type<?> type : typeMap.values()) {
343 if (type.hasPropertyValue(value) &&
344 (meaning == Meaning.INDEX_VALUE || meaning == type.getMeaning()) &&
345 type.getComparableFunction() != null) {
346 return toComparableObject(
347 type.getComparableFunction().asComparable(value));
350 return null;
354 * Converts the given {@link Object} into a supported value then returns it as
355 * a comparable object so it can be compared to other data types.
357 * @param value any Object that can be converted into a supported DataType
358 * @return {@code null} if value is null
359 * @throws UnsupportedOperationException if value is not supported
361 static Comparable<Object> getComparablePropertyValue(Object value) {
362 Pair<Type<?>, PropertyValue> newValue = createPropertyValue(value);
363 if (newValue.first != null) {
364 return toComparableObject(
365 newValue.first.getComparableFunction().asComparable(newValue.second));
367 return null;
371 * Isolating the warning suppression.
373 @SuppressWarnings("unchecked")
374 private static <T> Comparable<Object> toComparableObject(final Comparable<T> original) {
375 return (Comparable<Object>) original;
379 * Get the rank of the given datastore type relative to other datastore
380 * types. Note that datastore types do not necessarily have unique ranks.
382 @SuppressWarnings({"unchecked", "rawtypes"})
383 public static int getTypeRank(Class<? extends Comparable> datastoreType) {
384 return comparableTypeMap.get(datastoreType);
388 * Create a Property protobuffer that contains {@code propertyName}
389 * and {@code value}.
391 static Property toProperty(String propertyName, Object value) {
392 return createProperty(propertyName, value, false).second();
396 * Gets the {@link Type} that knows how to translate objects of
397 * type {@code clazz} into protocol buffers that the data store can
398 * handle.
399 * @throws UnsupportedOperationException if clazz is not supported
401 @SuppressWarnings("unchecked")
402 private static <T> Type<T> getType(Class<T> clazz) {
403 if (typeMap.containsKey(clazz)) {
404 return (Type<T>) typeMap.get(clazz);
405 } else {
406 throw new UnsupportedOperationException("Unsupported data type: " + clazz.getName());
411 * A function that converts a {@link PropertyValue} to its {@link
412 * Comparable} representation.
414 private interface AsComparableFunction {
415 Comparable<?> asComparable(PropertyValue pv);
419 * {@code Type} is an abstract class that knows how to convert Java
420 * objects of one or more types into a {@link PropertyValue}.
422 * @param <T> The canonical Java class for this type.
424 abstract static class Type<T> {
426 private final Meaning meaning;
428 protected Type(Meaning meaning) {
429 this.meaning = meaning;
432 protected Type() {
433 this(Meaning.NO_MEANING);
437 * Returns the {@link AsComparableFunction} for this {@code Type}, or
438 * {@code null} if values of this type are not comparable.
440 public abstract AsComparableFunction getComparableFunction();
443 * Returns the {@link Meaning} for this {@code Type}.
445 * @return A return value of {@link Meaning#NO_MEANING} indicates
446 * there is no special meaning.
448 public Meaning getMeaning() {
449 return meaning;
453 * Sets the value of {@code propertyValue} to {@code value}.
454 * If {@code value} is null, and the type of its {@link PropertyValue}
455 * attribute is primitive, then no update occurs.
457 public abstract void setPropertyValue(PropertyValue propertyValue, Object value);
460 * Returns the value of {@code propertyValue} as its canonical Java type.
462 * @param propertyValue a not {@code null} value representing this {@code Type}
463 * @return the standard representation of {@code value}. Will never be null
464 * for attributes of a primitive type.
465 * @throws NullPointerException if the property value doesn't exist.
466 * Use {@link #hasPropertyValue} first to determine if the property exists.
468 public abstract T getPropertyValue(PropertyValue propertyValue);
471 * Returns true if the property value exists.
473 * @param propertyValue a not {code null} property.
474 * @return true iff the property value has been set.
476 public abstract boolean hasPropertyValue(PropertyValue propertyValue);
480 * Used by {@link Type}s that do not support comparison.
482 private static final AsComparableFunction NOT_COMPARABLE = null;
485 * Converts a {@link PropertyValue} stored in the string field to a
486 * {@link Comparable}.
488 private static final AsComparableFunction COMP_BYTE_ARRAY_FUNC = new AsComparableFunction() {
489 @Override
490 public Comparable<ComparableByteArray> asComparable(PropertyValue pv) {
491 return new ComparableByteArray(pv.getStringValueAsBytes());
495 private static class StringType extends Type<String> {
497 @Override
498 public void setPropertyValue(PropertyValue propertyValue, Object value) {
499 propertyValue.setStringValue((String) value);
502 @Override
503 public String getPropertyValue(PropertyValue propertyValue) {
504 return propertyValue.getStringValue();
507 @Override
508 public boolean hasPropertyValue(PropertyValue propertyValue) {
509 return propertyValue.hasStringValue();
512 @Override
513 public AsComparableFunction getComparableFunction() {
514 return COMP_BYTE_ARRAY_FUNC;
519 * Converts a {@link PropertyValue} stored in the string field to a
520 * {@link Comparable}.
522 private static final AsComparableFunction COMP_RAW_VALUE_FUNC = new AsComparableFunction() {
523 @SuppressWarnings("unchecked")
524 @Override
525 public Comparable<?> asComparable(PropertyValue pv) {
526 if (pv.hasStringValue()) {
527 return new ComparableByteArray(pv.getStringValueAsBytes());
529 return (Comparable<Object>) new RawValue(pv).getValue();
533 private static class RawValueType extends Type<RawValue> {
535 @Override
536 public void setPropertyValue(PropertyValue propertyValue, Object value) {
537 throw new UnsupportedOperationException();
540 @Override
541 public RawValue getPropertyValue(PropertyValue propertyValue) {
542 return new RawValue(propertyValue);
545 @Override
546 public AsComparableFunction getComparableFunction() {
547 return COMP_RAW_VALUE_FUNC;
550 @Override
551 public boolean hasPropertyValue(PropertyValue propertyValue) {
552 return true;
555 @Override
556 public Meaning getMeaning() {
557 return Meaning.INDEX_VALUE;
562 * Converts a {@link PropertyValue} stored in the int64 field to a
563 * {@link Comparable}.
565 private static final AsComparableFunction INT_64_COMP_FUNC = new AsComparableFunction() {
566 @Override
567 public Comparable<Long> asComparable(PropertyValue pv) {
568 return pv.getInt64Value();
572 private static class Int64Type extends Type<Long> {
574 @Override
575 public void setPropertyValue(PropertyValue propertyValue, Object value) {
576 if (value != null) {
577 propertyValue.setInt64Value(((Number) value).longValue());
581 @Override
582 public Long getPropertyValue(PropertyValue propertyValue) {
583 return propertyValue.getInt64Value();
586 @Override
587 public boolean hasPropertyValue(PropertyValue propertyValue) {
588 return propertyValue.hasInt64Value();
591 @Override
592 public AsComparableFunction getComparableFunction() {
593 return INT_64_COMP_FUNC;
598 * Converts a {@link PropertyValue} stored in the double field to a
599 * {@link Comparable}.
601 private static final AsComparableFunction DOUBLE_COMP_FUNC = new AsComparableFunction() {
602 @Override
603 public Comparable<Double> asComparable(PropertyValue pv) {
604 return pv.getDoubleValue();
608 private static class DoubleType extends Type<Double> {
610 @Override
611 public void setPropertyValue(PropertyValue propertyValue, Object value) {
612 if (value != null) {
613 propertyValue.setDoubleValue(((Number) value).doubleValue());
617 @Override
618 public Double getPropertyValue(PropertyValue propertyValue) {
619 return propertyValue.hasDoubleValue() ? propertyValue.getDoubleValue() : null;
622 @Override
623 public boolean hasPropertyValue(PropertyValue propertyValue) {
624 return propertyValue.hasDoubleValue();
627 @Override
628 public AsComparableFunction getComparableFunction() {
629 return DOUBLE_COMP_FUNC;
634 * Converts a {@link PropertyValue} stored in the boolean field to a
635 * {@link Comparable}.
637 private static final AsComparableFunction BOOLEAN_COMP_FUNC = new AsComparableFunction() {
638 @Override
639 public Comparable<Boolean> asComparable(PropertyValue pv) {
640 return pv.isBooleanValue();
644 private static class BoolType extends Type<Boolean> {
646 @Override
647 public void setPropertyValue(PropertyValue propertyValue, Object value) {
648 if (value != null) {
649 propertyValue.setBooleanValue((Boolean) value);
653 @Override
654 public Boolean getPropertyValue(PropertyValue propertyValue) {
655 return propertyValue.isBooleanValue();
658 @Override
659 public boolean hasPropertyValue(PropertyValue propertyValue) {
660 return propertyValue.hasBooleanValue();
663 @Override
664 public AsComparableFunction getComparableFunction() {
665 return BOOLEAN_COMP_FUNC;
669 private static class UserType extends Type<User> {
671 private final AsComparableFunction USER_COMP_FUNC = new AsComparableFunction() {
672 @Override
673 public Comparable<User> asComparable(PropertyValue pv) {
674 return getPropertyValue(pv);
678 @Override
679 public void setPropertyValue(PropertyValue propertyValue, Object value) {
680 User user = (User) value;
681 UserValue userValue = new UserValue();
682 userValue.setEmail(user.getEmail());
683 userValue.setAuthDomain(user.getAuthDomain());
684 userValue.setGaiaid(0);
685 propertyValue.setUserValue(userValue);
688 @Override
689 public User getPropertyValue(PropertyValue propertyValue) {
690 UserValue userValue = propertyValue.getUserValue();
691 String userId = userValue.hasObfuscatedGaiaid() ? userValue.getObfuscatedGaiaid() : null;
692 return new User(userValue.getEmail(), userValue.getAuthDomain(), userId);
695 @Override
696 public boolean hasPropertyValue(PropertyValue propertyValue) {
697 return propertyValue.hasUserValue();
700 @Override
701 public AsComparableFunction getComparableFunction() {
702 return USER_COMP_FUNC;
706 private static class ReferenceType extends Type<Key> {
707 private final AsComparableFunction COMP_FUNC = new AsComparableFunction() {
708 @Override
709 public Comparable<Key> asComparable(PropertyValue pv) {
710 return getPropertyValue(pv);
714 @Override
715 public void setPropertyValue(PropertyValue propertyValue, Object value) {
716 Reference keyRef = KeyTranslator.convertToPb((Key) value);
717 setPropertyValue(propertyValue, keyRef);
720 private static void setPropertyValue(PropertyValue propertyValue, Reference keyRef) {
721 ReferenceValue refValue = new ReferenceValue();
722 refValue.setApp(keyRef.getApp());
723 if (keyRef.hasNameSpace()) {
724 refValue.setNameSpace(keyRef.getNameSpace());
726 Path path = keyRef.getPath();
727 for (Element element : path.elements()) {
728 ReferenceValuePathElement newElement = new ReferenceValuePathElement();
729 newElement.setType(element.getType());
730 if (element.hasName()) {
731 newElement.setName(element.getName());
733 if (element.hasId()) {
734 newElement.setId(element.getId());
736 refValue.addPathElement(newElement);
739 propertyValue.setReferenceValue(refValue);
742 @Override
743 public Key getPropertyValue(PropertyValue propertyValue) {
745 Reference reference = new Reference();
746 ReferenceValue refValue = propertyValue.getReferenceValue();
747 reference.setApp(refValue.getApp());
748 if (refValue.hasNameSpace()) {
749 reference.setNameSpace(refValue.getNameSpace());
751 Path path = new Path();
752 for (ReferenceValuePathElement element : refValue.pathElements()) {
753 Element newElement = new Element();
754 newElement.setType(element.getType());
755 if (element.hasName()) {
756 newElement.setName(element.getName());
758 if (element.hasId()) {
759 newElement.setId(element.getId());
761 path.addElement(newElement);
763 reference.setPath(path);
765 return KeyTranslator.createFromPb(reference);
768 @Override
769 public boolean hasPropertyValue(PropertyValue propertyValue) {
770 return propertyValue.hasReferenceValue();
773 @Override
774 public AsComparableFunction getComparableFunction() {
775 return COMP_FUNC;
779 private static class BlobType extends Type<Blob> {
780 private BlobType() {
781 super(Meaning.BLOB);
784 @Override
785 public void setPropertyValue(PropertyValue propertyValue, Object value) {
786 Blob blob = (Blob) value;
787 propertyValue.setStringValueAsBytes(blob.getBytes());
790 @Override
791 public Blob getPropertyValue(PropertyValue propertyValue) {
792 byte[] bytes = propertyValue.getStringValueAsBytes();
793 return new Blob(bytes);
796 @Override
797 public boolean hasPropertyValue(PropertyValue propertyValue) {
798 return propertyValue.hasStringValue();
801 @Override
802 public AsComparableFunction getComparableFunction() {
803 return NOT_COMPARABLE;
807 private static class EmbeddedEntityType extends Type<EmbeddedEntity> {
808 private EmbeddedEntityType() {
809 super(Meaning.ENTITY_PROTO);
812 @Override
813 public void setPropertyValue(PropertyValue propertyValue, Object value) {
814 EmbeddedEntity structProp = (EmbeddedEntity) value;
815 EntityProto proto = new EntityProto();
816 if (structProp.getKey() != null) {
817 proto.setKey(KeyTranslator.convertToPb(structProp.getKey()));
819 addPropertiesToPb(structProp.getPropertyMap(), proto);
820 propertyValue.setStringValueAsBytes(proto.toByteArray());
823 @Override
824 public EmbeddedEntity getPropertyValue(PropertyValue propertyValue) {
825 EntityProto proto = new EntityProto();
826 proto.mergeFrom(propertyValue.getStringValueAsBytes());
827 EmbeddedEntity result = new EmbeddedEntity();
828 if (proto.hasKey() && !proto.getKey().getApp().isEmpty()) {
829 result.setKey(KeyTranslator.createFromPb(proto.getKey()));
831 extractPropertiesFromPb(proto, result.getPropertyMap());
832 return result;
835 @Override
836 public boolean hasPropertyValue(PropertyValue propertyValue) {
837 return propertyValue.hasStringValue();
840 @Override
841 public AsComparableFunction getComparableFunction() {
842 return NOT_COMPARABLE;
846 private static class TextType extends Type<Text> {
847 private TextType() {
848 super(Meaning.TEXT);
851 @Override
852 public void setPropertyValue(PropertyValue propertyValue, Object value) {
853 Text text = (Text) value;
854 propertyValue.setStringValue(text.getValue());
857 @Override
858 public Text getPropertyValue(PropertyValue propertyValue) {
859 return new Text(propertyValue.getStringValue());
862 @Override
863 public boolean hasPropertyValue(PropertyValue propertyValue) {
864 return propertyValue.hasStringValue();
867 @Override
868 public AsComparableFunction getComparableFunction() {
869 return NOT_COMPARABLE;
873 private static class BlobKeyType extends CustomStringType<BlobKey> {
874 private BlobKeyType() {
875 super(Meaning.BLOBKEY);
878 @Override
879 protected String asDatastoreString(Object value) {
880 return ((BlobKey) value).getKeyString();
883 @Override
884 protected BlobKey fromDatastoreString(String datastoreString) {
885 return new BlobKey(datastoreString);
889 private static class DateType extends Type<Date> {
890 private DateType() {
891 super(Meaning.GD_WHEN);
894 @Override
895 public void setPropertyValue(PropertyValue propertyValue, Object value) {
896 Date date = (Date) value;
897 propertyValue.setInt64Value(date.getTime() * 1000L);
900 @Override
901 public Date getPropertyValue(PropertyValue propertyValue) {
902 return new Date(propertyValue.getInt64Value() / 1000L);
905 @Override
906 public AsComparableFunction getComparableFunction() {
907 return INT_64_COMP_FUNC;
910 @Override
911 public boolean hasPropertyValue(PropertyValue propertyValue) {
912 return propertyValue.hasInt64Value();
916 private static class LinkType extends Type<Link> {
917 private LinkType() {
918 super(Meaning.ATOM_LINK);
921 @Override
922 public void setPropertyValue(PropertyValue propertyValue, Object value) {
923 Link link = (Link) value;
924 propertyValue.setStringValue(link.getValue());
927 @Override
928 public Link getPropertyValue(PropertyValue propertyValue) {
929 return new Link(propertyValue.getStringValue());
932 @Override
933 public AsComparableFunction getComparableFunction() {
934 return COMP_BYTE_ARRAY_FUNC;
937 @Override
938 public boolean hasPropertyValue(PropertyValue propertyValue) {
939 return propertyValue.hasStringValue();
943 private static class ShortBlobType extends Type<ShortBlob> {
944 private ShortBlobType() {
945 super(Meaning.BYTESTRING);
948 @Override
949 public void setPropertyValue(PropertyValue propertyValue, Object value) {
950 propertyValue.setStringValueAsBytes(((ShortBlob) value).getBytes());
953 @Override
954 public ShortBlob getPropertyValue(PropertyValue propertyValue) {
955 return new ShortBlob(propertyValue.getStringValueAsBytes());
958 @Override
959 public AsComparableFunction getComparableFunction() {
960 return COMP_BYTE_ARRAY_FUNC;
963 @Override
964 public boolean hasPropertyValue(PropertyValue propertyValue) {
965 return propertyValue.hasStringValue();
970 * Base class for types that are basically just string wrappers.
972 * Delegates to a StringType instance because the parameterized type
973 * prevents us from extending StringType.
975 private static abstract class CustomStringType<T> extends Type<T> {
977 private CustomStringType(Meaning meaning) {
978 super(meaning);
981 @Override
982 public final void setPropertyValue(PropertyValue propertyValue, Object value) {
983 STRING_TYPE.setPropertyValue(propertyValue, asDatastoreString(value));
986 @Override
987 public final T getPropertyValue(PropertyValue propertyValue) {
988 return fromDatastoreString(STRING_TYPE.getPropertyValue(propertyValue));
991 @Override
992 public final AsComparableFunction getComparableFunction() {
993 return STRING_TYPE.getComparableFunction();
996 @Override
997 public final boolean hasPropertyValue(PropertyValue propertyValue) {
998 return STRING_TYPE.hasPropertyValue(propertyValue);
1001 protected abstract String asDatastoreString(Object value);
1002 protected abstract T fromDatastoreString(String datastoreString);
1006 * Base class for types that are basically just int64 wrappers.
1008 * Delegates to a Int64Type instance because the parameterized type
1009 * prevents us from extending Int64Type.
1011 private static abstract class CustomInt64Type<T> extends Type<T> {
1013 private static final Int64Type INT64_TYPE = new Int64Type();
1015 private CustomInt64Type(Meaning meaning) {
1016 super(meaning);
1019 @Override
1020 public final void setPropertyValue(PropertyValue propertyValue, Object value) {
1021 INT64_TYPE.setPropertyValue(propertyValue, asDatastoreLong(value));
1024 @Override
1025 public final T getPropertyValue(PropertyValue propertyValue) {
1026 return fromDatastoreLong(INT64_TYPE.getPropertyValue(propertyValue));
1029 @Override
1030 public final AsComparableFunction getComparableFunction() {
1031 return INT64_TYPE.getComparableFunction();
1034 @Override
1035 public final boolean hasPropertyValue(PropertyValue propertyValue) {
1036 return INT64_TYPE.hasPropertyValue(propertyValue);
1039 protected abstract Long asDatastoreLong(Object value);
1040 protected abstract T fromDatastoreLong(Long datastoreLong);
1044 * Internally a category is just a string with a special meaning.
1046 private static class CategoryType extends CustomStringType<Category> {
1047 private CategoryType() {
1048 super(Meaning.ATOM_CATEGORY);
1051 @Override
1052 protected String asDatastoreString(Object value) {
1053 return ((Category) value).getCategory();
1056 @Override
1057 protected Category fromDatastoreString(String datastoreString) {
1058 return new Category(datastoreString);
1063 * Internally a category is just an int64 with a special meaning.
1065 private static class RatingType extends CustomInt64Type<Rating> {
1067 private RatingType() {
1068 super(Meaning.GD_RATING);
1071 @Override
1072 protected Long asDatastoreLong(Object value) {
1073 return (long) ((Rating) value).getRating();
1076 @Override
1077 protected Rating fromDatastoreLong(Long datastoreLong) {
1078 if (datastoreLong == null) {
1079 throw new NullPointerException("rating value cannot be null");
1081 return new Rating(datastoreLong.intValue());
1086 * Internally an email is just a string with a special meaning.
1088 private static class EmailType extends CustomStringType<Email> {
1089 private EmailType() {
1090 super(Meaning.GD_EMAIL);
1093 @Override
1094 protected String asDatastoreString(Object value) {
1095 return ((Email) value).getEmail();
1098 @Override
1099 protected Email fromDatastoreString(String datastoreString) {
1100 return new Email(datastoreString);
1105 * Internally a postal address is just a string with a special meaning.
1107 private static class PostalAddressType extends CustomStringType<PostalAddress> {
1108 private PostalAddressType() {
1109 super(Meaning.GD_POSTALADDRESS);
1112 @Override
1113 protected String asDatastoreString(Object value) {
1114 return ((PostalAddress) value).getAddress();
1117 @Override
1118 protected PostalAddress fromDatastoreString(String datastoreString) {
1119 return new PostalAddress(datastoreString);
1124 * Internally a phone number is just a string with a special meaning.
1126 private static class PhoneNumberType extends CustomStringType<PhoneNumber> {
1127 private PhoneNumberType() {
1128 super(Meaning.GD_PHONENUMBER);
1131 @Override
1132 protected String asDatastoreString(Object value) {
1133 return ((PhoneNumber) value).getNumber();
1136 @Override
1137 protected PhoneNumber fromDatastoreString(String datastoreString) {
1138 return new PhoneNumber(datastoreString);
1143 * Internally an im handle is just a string with a special meaning and a
1144 * well known format.
1146 private static class IMHandleType extends CustomStringType<IMHandle> {
1148 private IMHandleType() {
1149 super(Meaning.GD_IM);
1152 @Override
1153 protected String asDatastoreString(Object value) {
1154 return ((IMHandle) value).toDatastoreString();
1157 @Override
1158 protected IMHandle fromDatastoreString(String datastoreString) {
1159 return IMHandle.fromDatastoreString(datastoreString);
1163 private static class GeoPtType extends Type<GeoPt> {
1165 private GeoPtType() {
1166 super(Meaning.GEORSS_POINT);
1170 * Converts a {@link PropertyValue} stored in the pointValue field to a
1171 * {@link Comparable}.
1173 private final AsComparableFunction POINT_VALUE_COMP_FUNC = new AsComparableFunction() {
1174 @Override
1175 public Comparable<GeoPt> asComparable(PropertyValue pv) {
1176 return getPropertyValue(pv);
1180 @Override
1181 public AsComparableFunction getComparableFunction() {
1182 return POINT_VALUE_COMP_FUNC;
1185 @Override
1186 public void setPropertyValue(PropertyValue propertyValue, Object value) {
1187 GeoPt geoPt = (GeoPt) value;
1188 PropertyValue.PointValue pv =
1189 new PropertyValue.PointValue().setX(geoPt.getLatitude()).setY(geoPt.getLongitude());
1190 propertyValue.setPointValue(pv);
1193 @Override
1194 public GeoPt getPropertyValue(PropertyValue propertyValue) {
1195 PropertyValue.PointValue pv = propertyValue.getPointValue();
1196 return new GeoPt((float) pv.getX(), (float) pv.getY());
1199 @Override
1200 public boolean hasPropertyValue(PropertyValue propertyValue) {
1201 return propertyValue.hasPointValue();
1205 private static class Pair<T, U> {
1207 private final T first;
1209 private final U second;
1211 Pair(T t, U u) {
1212 first = t;
1213 second = u;
1216 T first() {
1217 return first;
1220 U second() {
1221 return second;
1224 public static <T, U> Pair<T, U> of(T t, U u) {
1225 return new Pair<T, U>(t, u);
1229 static Map<Class<?>, Type<?>> getTypeMap() {
1230 return typeMap;
1234 * A wrapper for a {@code byte[]} that implements {@link Comparable}.
1235 * Comparison algorithm is the same as the prod datastore.
1237 public static final class ComparableByteArray implements Comparable<ComparableByteArray> {
1239 private final byte[] bytes;
1241 public ComparableByteArray(byte[] bytes) {
1242 this.bytes = bytes;
1245 @Override
1246 public int compareTo(ComparableByteArray other) {
1247 byte[] otherBytes = other.bytes;
1248 for (int i = 0; i < Math.min(bytes.length, otherBytes.length); i++) {
1249 int v1 = bytes[i] & 0xFF;
1250 int v2 = otherBytes[i] & 0xFF;
1251 if (v1 != v2) {
1252 return v1 - v2;
1255 return bytes.length - otherBytes.length;
1258 @Override
1259 public boolean equals(Object obj) {
1260 if (obj == null) {
1261 return false;
1263 return Arrays.equals(bytes, ((ComparableByteArray) obj).bytes);
1266 @Override
1267 public int hashCode() {
1268 int result = 1;
1269 for (byte b : bytes) {
1270 result = 31 * result + b;
1272 return result;
1276 private DataTypeTranslator() {