Revision created by MOE tool push_codebase.
[gae.git] / java / src / main / com / google / appengine / api / datastore / DataTypeUtils.java
blob72a821e7c0f83993b95116a62ec6ad5b83a216d3
1 // Copyright 2009 Google Inc. All rights reserved.
3 package com.google.appengine.api.datastore;
5 import static com.google.appengine.api.datastore.DataTypeUtils.CheckValueOption.ALLOW_MULTI_VALUE;
6 import static com.google.appengine.api.datastore.DataTypeUtils.CheckValueOption.REQUIRE_MULTI_VALUE;
7 import static com.google.appengine.api.datastore.DataTypeUtils.CheckValueOption.VALUE_PRE_CHECKED_WITHOUT_NAME;
8 import static java.nio.charset.StandardCharsets.UTF_8;
10 import com.google.appengine.api.blobstore.BlobKey;
11 import com.google.appengine.api.users.User;
13 import java.util.Collection;
14 import java.util.Collections;
15 import java.util.Date;
16 import java.util.EnumSet;
17 import java.util.HashSet;
18 import java.util.Set;
19 import java.util.logging.Logger;
21 /**
22 * {@code DataTypeUtils} presents a simpler interface that allows
23 * user-code to determine what Classes can safely be stored as
24 * properties in the data store.
26 * Currently this list includes:
27 * <ul>
28 * <li>{@link String} (but not {@link StringBuffer}),
30 * <li>All numeric primitive wrappers ({@link Byte} through {@link
31 * Long}, {@link Float} and {@link Double}, but not {@link
32 * java.math.BigInteger} or {@link java.math.BigDecimal}.
34 * <li>{@link Key}, for storing references to other {@link Entity}
35 * objects.
37 * <li>{@link User}, for storing references to users.
39 * <li>{@link ShortBlob}, for storing binary data small enough to be indexed.
40 * This means properties of this type, unlike {@link Blob} properties, can be
41 * filtered and sorted on in queries.
43 * <li>{@link Blob}, for storing unindexed binary data less than 1MB.
45 * <li>{@link Text}, for storing unindexed String data less than 1MB.
47 * <li>{@link BlobKey}, for storing references to user uploaded
48 * blobs (which may exceed 1MB).
50 * <li>{@link Date}.
52 * <li>{@link Link}.
53 * </ul>
56 public final class DataTypeUtils {
58 /**
59 * Options for checking if a property value is valid for a property.
61 enum CheckValueOption {
63 /**
64 * Allow the value to be a collection of values.
66 ALLOW_MULTI_VALUE,
68 /**
69 * Require the value to be a collection of values.
71 REQUIRE_MULTI_VALUE,
73 /**
74 * The value's validity has already been checked but not in conjunction with the property
75 * name.
77 VALUE_PRE_CHECKED_WITHOUT_NAME
80 private static final Logger logger = Logger.getLogger(DataTypeUtils.class.getName());
82 /**
83 * This is the maximum number of bytes that a string property
84 * can contain. If your string has more bytes, you need to
85 * wrap it in a {@link Text}.
87 public static final int MAX_STRING_PROPERTY_LENGTH = 1500;
89 /**
90 * This is the maximum number of bytes that a {@code ShortBlob} property
91 * can contain. If your data is larger, you need to use a {@code Blob}.
93 public static final int MAX_SHORT_BLOB_PROPERTY_LENGTH = 1500;
95 public static final int MAX_LINK_PROPERTY_LENGTH = 2083;
97 private static final Set<Class<?>> SUPPORTED_TYPES = new HashSet<Class<?>>();
98 static {
99 Collections.addAll(SUPPORTED_TYPES,
100 RawValue.class,
101 Boolean.class,
102 String.class,
103 Byte.class,
104 Short.class,
105 Integer.class,
106 Long.class,
107 Float.class,
108 Double.class,
109 User.class,
110 Key.class,
111 Blob.class,
112 Text.class,
113 Date.class,
114 Link.class,
115 ShortBlob.class,
116 GeoPt.class,
117 Category.class,
118 Rating.class,
119 PhoneNumber.class,
120 PostalAddress.class,
121 Email.class,
122 IMHandle.class,
123 BlobKey.class,
124 EmbeddedEntity.class);
128 * If the specified object cannot be used as the value for a {@code
129 * Entity} property, throw an exception with the appropriate
130 * explanation.
132 * @throws NullPointerException if the specified value is null
133 * @throws IllegalArgumentException if the type is not supported, or
134 * if the object is in some other way invalid.
136 public static void checkSupportedValue(Object value) {
137 checkSupportedValue(null, value);
141 * If the specified object cannot be used as the value for a {@code
142 * Entity} property, throw an exception with the appropriate
143 * explanation.
145 * @throws NullPointerException if the specified value is null
146 * @throws IllegalArgumentException if the type is not supported, or
147 * if the object is in some other way invalid.
149 public static void checkSupportedValue(String name, Object value) {
150 checkSupportedValue(name, value, true, false);
154 * If the specified object cannot be used as the value for a {@code
155 * Entity} property, throw an exception with the appropriate
156 * explanation.
158 * @param name name of the property
159 * @param value value in question
160 * @param allowMultiValue if this property allows multivalue values
161 * @param requireMultiValue if this property requires multivalue values
163 * @throws IllegalArgumentException if the type is not supported, or
164 * if the object is in some other way invalid.
166 static void checkSupportedValue(String name, Object value,
167 boolean allowMultiValue, boolean requireMultiValue) {
168 EnumSet<CheckValueOption> options = EnumSet.noneOf(CheckValueOption.class);
169 if (allowMultiValue) {
170 options.add(ALLOW_MULTI_VALUE);
172 if (requireMultiValue) {
173 options.add(REQUIRE_MULTI_VALUE);
175 checkSupportedValue(name, value, options, SUPPORTED_TYPES);
179 * If the specified object cannot be used as the value for a {@code
180 * Entity} property, throw an exception with the appropriate
181 * explanation.
183 * @param name name of the property
184 * @param value value in question
185 * @param options the options for this check invocation.
186 * @param supportedTypes the types considered to be valid types for the value.
188 * @throws IllegalArgumentException if the type is not supported, or
189 * if the object is in some other way invalid.
191 static void checkSupportedValue(String name, Object value, EnumSet<CheckValueOption> options,
192 Set<Class<?>> supportedTypes) {
193 if (value instanceof Collection<?>) {
194 if (!options.contains(ALLOW_MULTI_VALUE)) {
195 throw new IllegalArgumentException("A collection of values is not allowed.");
198 Collection<?> values = (Collection<?>) value;
199 if (!values.isEmpty()) {
200 for (Object obj : values) {
201 checkSupportedSingleValue(name, obj, options, supportedTypes);
203 } else if (options.contains(REQUIRE_MULTI_VALUE)) {
204 throw new IllegalArgumentException("A collection with at least one value is required.");
206 } else if (options.contains(REQUIRE_MULTI_VALUE)) {
207 throw new IllegalArgumentException("A collection of values is required.");
208 } else {
209 checkSupportedSingleValue(name, value, options, supportedTypes);
213 private static void checkSupportedSingleValue(String name, Object value,
214 EnumSet<CheckValueOption> options, Set<Class<?>> supportedTypes) {
215 if (value == null) {
216 return;
219 if (Entity.KEY_RESERVED_PROPERTY.equals(name)) {
220 if (!(value instanceof Key)) {
221 logger.warning(Entity.KEY_RESERVED_PROPERTY + " value should be of type Key");
225 if (options.contains(VALUE_PRE_CHECKED_WITHOUT_NAME)) {
226 return;
229 String prefix;
230 if (name == null) {
231 prefix = "";
232 } else {
233 prefix = name + ": ";
236 if (!supportedTypes.contains(value.getClass())) {
237 throw new IllegalArgumentException(
238 prefix + value.getClass().getName() + " is not a supported property type.");
241 if (value instanceof String) {
242 int length = ((String) value).getBytes(UTF_8).length;
243 if (length > MAX_STRING_PROPERTY_LENGTH) {
244 throw new IllegalArgumentException(
245 prefix + "String properties must be " + MAX_STRING_PROPERTY_LENGTH
246 + " bytes or less. Instead, use " + Text.class.getName() + ", which can store "
247 + "strings of any length.");
249 } else if (value instanceof Link) {
250 int length = ((Link) value).getValue().getBytes(UTF_8).length;
251 if (length > MAX_LINK_PROPERTY_LENGTH) {
252 throw new IllegalArgumentException(
253 prefix + "Link properties must be " + MAX_LINK_PROPERTY_LENGTH
254 + " bytes or less. Instead, use " + Text.class.getName() + ", which can store "
255 + "strings of any length.");
257 } else if (value instanceof ShortBlob) {
258 int length = ((ShortBlob) value).getBytes().length;
259 if (length > MAX_SHORT_BLOB_PROPERTY_LENGTH) {
260 throw new IllegalArgumentException(prefix + "byte[] properties must be "
261 + MAX_SHORT_BLOB_PROPERTY_LENGTH
262 + " bytes or less. Instead, use " + Blob.class.getName() + ", which can store binary "
263 + "data of any size.");
269 * Returns true if and only if the supplied {@code Class} can be
270 * stored in the data store.
272 public static boolean isSupportedType(Class<?> clazz) {
273 return SUPPORTED_TYPES.contains(clazz);
277 * Returns an unmodifiable {@code Set} of supported {@code Class}
278 * objects.
280 public static Set<Class<?>> getSupportedTypes() {
281 return Collections.unmodifiableSet(SUPPORTED_TYPES);
284 private DataTypeUtils() {