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
;
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();
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();
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();
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.
93 Map
<Class
<?
extends Comparable
<?
>>, Integer
> comparableTypeMap
=
94 new HashMap
<Class
<?
extends Comparable
<?
>>, Integer
>();
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);
131 for (Object listValue
: values
) {
132 addProperty(proto
, key
, listValue
, indexed
, true);
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
);
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
,
177 Property property
= new Property();
178 property
.setName(name
);
179 property
.setMultiple(multiple
);
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
) {
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}
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}
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}
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}
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
);
258 * Locates and returns all indexed properties with the given name on the
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()) {
291 matchingMultipleProps
.add(prop
);
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
));
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
);
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
));
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
));
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}
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
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
);
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
;
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() {
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() {
490 public Comparable
<ComparableByteArray
> asComparable(PropertyValue pv
) {
491 return new ComparableByteArray(pv
.getStringValueAsBytes());
495 private static class StringType
extends Type
<String
> {
498 public void setPropertyValue(PropertyValue propertyValue
, Object value
) {
499 propertyValue
.setStringValue((String
) value
);
503 public String
getPropertyValue(PropertyValue propertyValue
) {
504 return propertyValue
.getStringValue();
508 public boolean hasPropertyValue(PropertyValue propertyValue
) {
509 return propertyValue
.hasStringValue();
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")
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
> {
536 public void setPropertyValue(PropertyValue propertyValue
, Object value
) {
537 throw new UnsupportedOperationException();
541 public RawValue
getPropertyValue(PropertyValue propertyValue
) {
542 return new RawValue(propertyValue
);
546 public AsComparableFunction
getComparableFunction() {
547 return COMP_RAW_VALUE_FUNC
;
551 public boolean hasPropertyValue(PropertyValue propertyValue
) {
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() {
567 public Comparable
<Long
> asComparable(PropertyValue pv
) {
568 return pv
.getInt64Value();
572 private static class Int64Type
extends Type
<Long
> {
575 public void setPropertyValue(PropertyValue propertyValue
, Object value
) {
577 propertyValue
.setInt64Value(((Number
) value
).longValue());
582 public Long
getPropertyValue(PropertyValue propertyValue
) {
583 return propertyValue
.getInt64Value();
587 public boolean hasPropertyValue(PropertyValue propertyValue
) {
588 return propertyValue
.hasInt64Value();
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() {
603 public Comparable
<Double
> asComparable(PropertyValue pv
) {
604 return pv
.getDoubleValue();
608 private static class DoubleType
extends Type
<Double
> {
611 public void setPropertyValue(PropertyValue propertyValue
, Object value
) {
613 propertyValue
.setDoubleValue(((Number
) value
).doubleValue());
618 public Double
getPropertyValue(PropertyValue propertyValue
) {
619 return propertyValue
.hasDoubleValue() ? propertyValue
.getDoubleValue() : null;
623 public boolean hasPropertyValue(PropertyValue propertyValue
) {
624 return propertyValue
.hasDoubleValue();
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() {
639 public Comparable
<Boolean
> asComparable(PropertyValue pv
) {
640 return pv
.isBooleanValue();
644 private static class BoolType
extends Type
<Boolean
> {
647 public void setPropertyValue(PropertyValue propertyValue
, Object value
) {
649 propertyValue
.setBooleanValue((Boolean
) value
);
654 public Boolean
getPropertyValue(PropertyValue propertyValue
) {
655 return propertyValue
.isBooleanValue();
659 public boolean hasPropertyValue(PropertyValue propertyValue
) {
660 return propertyValue
.hasBooleanValue();
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() {
673 public Comparable
<User
> asComparable(PropertyValue pv
) {
674 return getPropertyValue(pv
);
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
);
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
);
696 public boolean hasPropertyValue(PropertyValue propertyValue
) {
697 return propertyValue
.hasUserValue();
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() {
709 public Comparable
<Key
> asComparable(PropertyValue pv
) {
710 return getPropertyValue(pv
);
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
);
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
);
769 public boolean hasPropertyValue(PropertyValue propertyValue
) {
770 return propertyValue
.hasReferenceValue();
774 public AsComparableFunction
getComparableFunction() {
779 private static class BlobType
extends Type
<Blob
> {
785 public void setPropertyValue(PropertyValue propertyValue
, Object value
) {
786 Blob blob
= (Blob
) value
;
787 propertyValue
.setStringValueAsBytes(blob
.getBytes());
791 public Blob
getPropertyValue(PropertyValue propertyValue
) {
792 byte[] bytes
= propertyValue
.getStringValueAsBytes();
793 return new Blob(bytes
);
797 public boolean hasPropertyValue(PropertyValue propertyValue
) {
798 return propertyValue
.hasStringValue();
802 public AsComparableFunction
getComparableFunction() {
803 return NOT_COMPARABLE
;
807 private static class EmbeddedEntityType
extends Type
<EmbeddedEntity
> {
808 private EmbeddedEntityType() {
809 super(Meaning
.ENTITY_PROTO
);
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());
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());
836 public boolean hasPropertyValue(PropertyValue propertyValue
) {
837 return propertyValue
.hasStringValue();
841 public AsComparableFunction
getComparableFunction() {
842 return NOT_COMPARABLE
;
846 private static class TextType
extends Type
<Text
> {
852 public void setPropertyValue(PropertyValue propertyValue
, Object value
) {
853 Text text
= (Text
) value
;
854 propertyValue
.setStringValue(text
.getValue());
858 public Text
getPropertyValue(PropertyValue propertyValue
) {
859 return new Text(propertyValue
.getStringValue());
863 public boolean hasPropertyValue(PropertyValue propertyValue
) {
864 return propertyValue
.hasStringValue();
868 public AsComparableFunction
getComparableFunction() {
869 return NOT_COMPARABLE
;
873 private static class BlobKeyType
extends CustomStringType
<BlobKey
> {
874 private BlobKeyType() {
875 super(Meaning
.BLOBKEY
);
879 protected String
asDatastoreString(Object value
) {
880 return ((BlobKey
) value
).getKeyString();
884 protected BlobKey
fromDatastoreString(String datastoreString
) {
885 return new BlobKey(datastoreString
);
889 private static class DateType
extends Type
<Date
> {
891 super(Meaning
.GD_WHEN
);
895 public void setPropertyValue(PropertyValue propertyValue
, Object value
) {
896 Date date
= (Date
) value
;
897 propertyValue
.setInt64Value(date
.getTime() * 1000L);
901 public Date
getPropertyValue(PropertyValue propertyValue
) {
902 return new Date(propertyValue
.getInt64Value() / 1000L);
906 public AsComparableFunction
getComparableFunction() {
907 return INT_64_COMP_FUNC
;
911 public boolean hasPropertyValue(PropertyValue propertyValue
) {
912 return propertyValue
.hasInt64Value();
916 private static class LinkType
extends Type
<Link
> {
918 super(Meaning
.ATOM_LINK
);
922 public void setPropertyValue(PropertyValue propertyValue
, Object value
) {
923 Link link
= (Link
) value
;
924 propertyValue
.setStringValue(link
.getValue());
928 public Link
getPropertyValue(PropertyValue propertyValue
) {
929 return new Link(propertyValue
.getStringValue());
933 public AsComparableFunction
getComparableFunction() {
934 return COMP_BYTE_ARRAY_FUNC
;
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
);
949 public void setPropertyValue(PropertyValue propertyValue
, Object value
) {
950 propertyValue
.setStringValueAsBytes(((ShortBlob
) value
).getBytes());
954 public ShortBlob
getPropertyValue(PropertyValue propertyValue
) {
955 return new ShortBlob(propertyValue
.getStringValueAsBytes());
959 public AsComparableFunction
getComparableFunction() {
960 return COMP_BYTE_ARRAY_FUNC
;
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
) {
982 public final void setPropertyValue(PropertyValue propertyValue
, Object value
) {
983 STRING_TYPE
.setPropertyValue(propertyValue
, asDatastoreString(value
));
987 public final T
getPropertyValue(PropertyValue propertyValue
) {
988 return fromDatastoreString(STRING_TYPE
.getPropertyValue(propertyValue
));
992 public final AsComparableFunction
getComparableFunction() {
993 return STRING_TYPE
.getComparableFunction();
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
) {
1020 public final void setPropertyValue(PropertyValue propertyValue
, Object value
) {
1021 INT64_TYPE
.setPropertyValue(propertyValue
, asDatastoreLong(value
));
1025 public final T
getPropertyValue(PropertyValue propertyValue
) {
1026 return fromDatastoreLong(INT64_TYPE
.getPropertyValue(propertyValue
));
1030 public final AsComparableFunction
getComparableFunction() {
1031 return INT64_TYPE
.getComparableFunction();
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
);
1052 protected String
asDatastoreString(Object value
) {
1053 return ((Category
) value
).getCategory();
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
);
1072 protected Long
asDatastoreLong(Object value
) {
1073 return (long) ((Rating
) value
).getRating();
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
);
1094 protected String
asDatastoreString(Object value
) {
1095 return ((Email
) value
).getEmail();
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
);
1113 protected String
asDatastoreString(Object value
) {
1114 return ((PostalAddress
) value
).getAddress();
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
);
1132 protected String
asDatastoreString(Object value
) {
1133 return ((PhoneNumber
) value
).getNumber();
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
);
1153 protected String
asDatastoreString(Object value
) {
1154 return ((IMHandle
) value
).toDatastoreString();
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() {
1175 public Comparable
<GeoPt
> asComparable(PropertyValue pv
) {
1176 return getPropertyValue(pv
);
1181 public AsComparableFunction
getComparableFunction() {
1182 return POINT_VALUE_COMP_FUNC
;
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
);
1194 public GeoPt
getPropertyValue(PropertyValue propertyValue
) {
1195 PropertyValue
.PointValue pv
= propertyValue
.getPointValue();
1196 return new GeoPt((float) pv
.getX(), (float) pv
.getY());
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
;
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() {
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
) {
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;
1255 return bytes
.length
- otherBytes
.length
;
1259 public boolean equals(Object obj
) {
1263 return Arrays
.equals(bytes
, ((ComparableByteArray
) obj
).bytes
);
1267 public int hashCode() {
1269 for (byte b
: bytes
) {
1270 result
= 31 * result
+ b
;
1276 private DataTypeTranslator() {