Revision created by MOE tool push_codebase.
[gae.git] / java / src / main / com / google / appengine / api / datastore / DataTypeUtils.java
blob9a2969273cd168b8df9206033d7a4105d611ce00
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;
9 import com.google.appengine.api.blobstore.BlobKey;
10 import com.google.appengine.api.users.User;
12 import java.util.Collection;
13 import java.util.Collections;
14 import java.util.Date;
15 import java.util.EnumSet;
16 import java.util.HashSet;
17 import java.util.Set;
18 import java.util.logging.Logger;
20 /**
21 * {@code DataTypeUtils} presents a simpler interface that allows
22 * user-code to determine what Classes can safely be stored as
23 * properties in the data store.
25 * Currently this list includes:
26 * <ul>
27 * <li>{@link String} (but not {@link StringBuffer}),
29 * <li>All numeric primitive wrappers ({@link Byte} through {@link
30 * Long}, {@link Float} and {@link Double}, but not {@link
31 * java.math.BigInteger} or {@link java.math.BigDecimal}.
33 * <li>{@link Key}, for storing references to other {@link Entity}
34 * objects.
36 * <li>{@link User}, for storing references to users.
38 * <li>{@link ShortBlob}, for storing binary data small enough to be indexed.
39 * This means properties of this type, unlike {@link Blob} properties, can be
40 * filtered and sorted on in queries.
42 * <li>{@link Blob}, for storing unindexed binary data less than 1MB.
44 * <li>{@link Text}, for storing unindexed String data less than 1MB.
46 * <li>{@link BlobKey}, for storing references to user uploaded
47 * blobs (which may exceed 1MB).
49 * <li>{@link Date}.
51 * <li>{@link Link}.
52 * </ul>
55 public final class DataTypeUtils {
57 /**
58 * Options for checking if a property value is valid for a property.
60 enum CheckValueOption {
62 /**
63 * Allow the value to be a collection of values.
65 ALLOW_MULTI_VALUE,
67 /**
68 * Require the value to be a collection of values.
70 REQUIRE_MULTI_VALUE,
72 /**
73 * The value's validity has already been checked but not in conjunction with the property
74 * name.
76 VALUE_PRE_CHECKED_WITHOUT_NAME
79 private static final Logger logger = Logger.getLogger(DataTypeUtils.class.getName());
81 /**
82 * This is the maximum number of characters that a string property
83 * can contain. If your string has more characters, you need to
84 * wrap it in a {@link Text}.
86 public static final int MAX_STRING_PROPERTY_LENGTH = 500;
88 /**
89 * This is the maximum number of bytes that a {@code ShortBlob} property
90 * can contain. If your data is larger, you need to use a {@code Blob}.
92 public static final int MAX_SHORT_BLOB_PROPERTY_LENGTH = 500;
94 public static final int MAX_LINK_PROPERTY_LENGTH = 2083;
96 private static final Set<Class<?>> SUPPORTED_TYPES = new HashSet<Class<?>>();
97 static {
98 Collections.addAll(SUPPORTED_TYPES,
99 RawValue.class,
100 Boolean.class,
101 String.class,
102 Byte.class,
103 Short.class,
104 Integer.class,
105 Long.class,
106 Float.class,
107 Double.class,
108 User.class,
109 Key.class,
110 Blob.class,
111 Text.class,
112 Date.class,
113 Link.class,
114 ShortBlob.class,
115 GeoPt.class,
116 Category.class,
117 Rating.class,
118 PhoneNumber.class,
119 PostalAddress.class,
120 Email.class,
121 IMHandle.class,
122 BlobKey.class,
123 EmbeddedEntity.class);
127 * If the specified object cannot be used as the value for a {@code
128 * Entity} property, throw an exception with the appropriate
129 * explanation.
131 * @throws NullPointerException if the specified value is null
132 * @throws IllegalArgumentException if the type is not supported, or
133 * if the object is in some other way invalid.
135 public static void checkSupportedValue(Object value) {
136 checkSupportedValue(null, value);
140 * If the specified object cannot be used as the value for a {@code
141 * Entity} property, throw an exception with the appropriate
142 * explanation.
144 * @throws NullPointerException if the specified value is null
145 * @throws IllegalArgumentException if the type is not supported, or
146 * if the object is in some other way invalid.
148 public static void checkSupportedValue(String name, Object value) {
149 checkSupportedValue(name, value, true, false);
153 * If the specified object cannot be used as the value for a {@code
154 * Entity} property, throw an exception with the appropriate
155 * explanation.
157 * @param name name of the property
158 * @param value value in question
159 * @param allowMultiValue if this property allows multivalue values
160 * @param requireMultiValue if this property requires multivalue values
162 * @throws IllegalArgumentException if the type is not supported, or
163 * if the object is in some other way invalid.
165 static void checkSupportedValue(String name, Object value,
166 boolean allowMultiValue, boolean requireMultiValue) {
167 EnumSet<CheckValueOption> options = EnumSet.noneOf(CheckValueOption.class);
168 if (allowMultiValue) {
169 options.add(ALLOW_MULTI_VALUE);
171 if (requireMultiValue) {
172 options.add(REQUIRE_MULTI_VALUE);
174 checkSupportedValue(name, value, options, SUPPORTED_TYPES);
178 * If the specified object cannot be used as the value for a {@code
179 * Entity} property, throw an exception with the appropriate
180 * explanation.
182 * @param name name of the property
183 * @param value value in question
184 * @param options the options for this check invocation.
185 * @param supportedTypes the types considered to be valid types for the value.
187 * @throws IllegalArgumentException if the type is not supported, or
188 * if the object is in some other way invalid.
190 static void checkSupportedValue(String name, Object value, EnumSet<CheckValueOption> options,
191 Set<Class<?>> supportedTypes) {
192 if (value instanceof Collection<?>) {
193 if (!options.contains(ALLOW_MULTI_VALUE)) {
194 throw new IllegalArgumentException("A collection of values is not allowed.");
197 Collection<?> values = (Collection<?>) value;
198 if (!values.isEmpty()) {
199 for (Object obj : values) {
200 checkSupportedSingleValue(name, obj, options, supportedTypes);
202 } else if (options.contains(REQUIRE_MULTI_VALUE)) {
203 throw new IllegalArgumentException("A collection with at least one value is required.");
205 } else if (options.contains(REQUIRE_MULTI_VALUE)) {
206 throw new IllegalArgumentException("A collection of values is required.");
207 } else {
208 checkSupportedSingleValue(name, value, options, supportedTypes);
212 private static void checkSupportedSingleValue(String name, Object value,
213 EnumSet<CheckValueOption> options, Set<Class<?>> supportedTypes) {
214 if (value == null) {
215 return;
218 if (Entity.KEY_RESERVED_PROPERTY.equals(name)) {
219 if (!(value instanceof Key)) {
220 logger.warning(Entity.KEY_RESERVED_PROPERTY + " value should be of type Key");
224 if (options.contains(VALUE_PRE_CHECKED_WITHOUT_NAME)) {
225 return;
228 String prefix;
229 if (name == null) {
230 prefix = "";
231 } else {
232 prefix = name + ": ";
235 if (!supportedTypes.contains(value.getClass())) {
236 throw new IllegalArgumentException(
237 prefix + value.getClass().getName() + " is not a supported property type.");
240 if (value instanceof String) {
241 int length = ((String) value).length();
242 if (length > MAX_STRING_PROPERTY_LENGTH) {
243 throw new IllegalArgumentException(
244 prefix + "String properties must be " + MAX_STRING_PROPERTY_LENGTH
245 + " characters or less. Instead, use " + Text.class.getName() + ", which can store "
246 + "strings of any length.");
248 } else if (value instanceof Link) {
249 int length = ((Link) value).getValue().length();
250 if (length > MAX_LINK_PROPERTY_LENGTH) {
251 throw new IllegalArgumentException(
252 prefix + "Link properties must be " + MAX_LINK_PROPERTY_LENGTH
253 + " characters or less. Instead, use " + Text.class.getName() + ", which can store "
254 + "strings of any length.");
256 } else if (value instanceof ShortBlob) {
257 int length = ((ShortBlob) value).getBytes().length;
258 if (length > MAX_SHORT_BLOB_PROPERTY_LENGTH) {
259 throw new IllegalArgumentException(prefix + "byte[] properties must be "
260 + MAX_SHORT_BLOB_PROPERTY_LENGTH
261 + " bytes or less. Instead, use " + Blob.class.getName() + ", which can store binary "
262 + "data of any size.");
268 * Returns true if and only if the supplied {@code Class} can be
269 * stored in the data store.
271 public static boolean isSupportedType(Class<?> clazz) {
272 return SUPPORTED_TYPES.contains(clazz);
276 * Returns an unmodifiable {@code Set} of supported {@code Class}
277 * objects.
279 public static Set<Class<?>> getSupportedTypes() {
280 return Collections.unmodifiableSet(SUPPORTED_TYPES);
283 private DataTypeUtils() {