Revision created by MOE tool push_codebase.
[gae.git] / java / src / main / com / google / appengine / api / datastore / DataTypeUtils.java
blobf78afbc442136aa3de1037942f9ef4ed8c52d774
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 = 2038;
96 private static final Set<Class<?>> SUPPORTED_TYPES = new HashSet<Class<?>>();
97 static {
98 SUPPORTED_TYPES.add(RawValue.class);
99 SUPPORTED_TYPES.add(Boolean.class);
100 SUPPORTED_TYPES.add(String.class);
101 SUPPORTED_TYPES.add(Byte.class);
102 SUPPORTED_TYPES.add(Short.class);
103 SUPPORTED_TYPES.add(Integer.class);
104 SUPPORTED_TYPES.add(Long.class);
105 SUPPORTED_TYPES.add(Float.class);
106 SUPPORTED_TYPES.add(Double.class);
107 SUPPORTED_TYPES.add(User.class);
108 SUPPORTED_TYPES.add(Key.class);
109 SUPPORTED_TYPES.add(Blob.class);
110 SUPPORTED_TYPES.add(Text.class);
111 SUPPORTED_TYPES.add(Date.class);
112 SUPPORTED_TYPES.add(Link.class);
113 SUPPORTED_TYPES.add(ShortBlob.class);
114 SUPPORTED_TYPES.add(GeoPt.class);
115 SUPPORTED_TYPES.add(Category.class);
116 SUPPORTED_TYPES.add(Rating.class);
117 SUPPORTED_TYPES.add(PhoneNumber.class);
118 SUPPORTED_TYPES.add(PostalAddress.class);
119 SUPPORTED_TYPES.add(Email.class);
120 SUPPORTED_TYPES.add(IMHandle.class);
121 SUPPORTED_TYPES.add(BlobKey.class);
122 SUPPORTED_TYPES.add(EmbeddedEntity.class);
126 * If the specified object cannot be used as the value for a {@code
127 * Entity} property, throw an exception with the appropriate
128 * explanation.
130 * @throws NullPointerException if the specified value is null
131 * @throws IllegalArgumentException if the type is not supported, or
132 * if the object is in some other way invalid.
134 public static void checkSupportedValue(Object value) {
135 checkSupportedValue(null, value);
139 * If the specified object cannot be used as the value for a {@code
140 * Entity} property, throw an exception with the appropriate
141 * explanation.
143 * @throws NullPointerException if the specified value is null
144 * @throws IllegalArgumentException if the type is not supported, or
145 * if the object is in some other way invalid.
147 public static void checkSupportedValue(String name, Object value) {
148 checkSupportedValue(name, value, true, false);
152 * If the specified object cannot be used as the value for a {@code
153 * Entity} property, throw an exception with the appropriate
154 * explanation.
156 * @param name name of the property
157 * @param value value in question
158 * @param allowMultiValue if this property allows multivalue values
159 * @param requireMultiValue if this property requires multivalue values
161 * @throws IllegalArgumentException if the type is not supported, or
162 * if the object is in some other way invalid.
164 static void checkSupportedValue(String name, Object value,
165 boolean allowMultiValue, boolean requireMultiValue) {
166 EnumSet<CheckValueOption> options = EnumSet.noneOf(CheckValueOption.class);
167 if (allowMultiValue) {
168 options.add(ALLOW_MULTI_VALUE);
170 if (requireMultiValue) {
171 options.add(REQUIRE_MULTI_VALUE);
173 checkSupportedValue(name, value, options, SUPPORTED_TYPES);
177 * If the specified object cannot be used as the value for a {@code
178 * Entity} property, throw an exception with the appropriate
179 * explanation.
181 * @param name name of the property
182 * @param value value in question
183 * @param options the options for this check invocation.
184 * @param supportedTypes the types considered to be valid types for the value.
186 * @throws IllegalArgumentException if the type is not supported, or
187 * if the object is in some other way invalid.
189 static void checkSupportedValue(String name, Object value, EnumSet<CheckValueOption> options,
190 Set<Class<?>> supportedTypes) {
191 if (value instanceof Collection<?>) {
192 if (!options.contains(ALLOW_MULTI_VALUE)) {
193 throw new IllegalArgumentException("A collection of values is not allowed.");
196 Collection<?> values = (Collection<?>) value;
197 if (!values.isEmpty()) {
198 for (Object obj : values) {
199 checkSupportedSingleValue(name, obj, options, supportedTypes);
201 } else if (options.contains(REQUIRE_MULTI_VALUE)) {
202 throw new IllegalArgumentException("A collection with at least one value is required.");
204 } else if (options.contains(REQUIRE_MULTI_VALUE)) {
205 throw new IllegalArgumentException("A collection of values is required.");
206 } else {
207 checkSupportedSingleValue(name, value, options, supportedTypes);
211 private static void checkSupportedSingleValue(String name, Object value,
212 EnumSet<CheckValueOption> options, Set<Class<?>> supportedTypes) {
213 if (value == null) {
214 return;
217 if (Entity.KEY_RESERVED_PROPERTY.equals(name)) {
218 if (!(value instanceof Key)) {
219 logger.warning(Entity.KEY_RESERVED_PROPERTY + " value should be of type Key");
223 if (options.contains(VALUE_PRE_CHECKED_WITHOUT_NAME)) {
224 return;
227 String prefix;
228 if (name == null) {
229 prefix = "";
230 } else {
231 prefix = name + ": ";
234 if (!supportedTypes.contains(value.getClass())) {
235 throw new IllegalArgumentException(
236 prefix + value.getClass().getName() + " is not a supported property type.");
239 if (value instanceof String) {
240 int length = ((String) value).length();
241 if (length > MAX_STRING_PROPERTY_LENGTH) {
242 throw new IllegalArgumentException(
243 prefix + "String properties must be " + MAX_STRING_PROPERTY_LENGTH
244 + " characters or less. Instead, use " + Text.class.getName() + ", which can store "
245 + "strings of any length.");
247 } else if (value instanceof Link) {
248 int length = ((Link) value).getValue().length();
249 if (length > MAX_LINK_PROPERTY_LENGTH) {
250 throw new IllegalArgumentException(
251 prefix + "Link properties must be " + MAX_LINK_PROPERTY_LENGTH
252 + " characters or less. Instead, use " + Text.class.getName() + ", which can store "
253 + "strings of any length.");
255 } else if (value instanceof ShortBlob) {
256 int length = ((ShortBlob) value).getBytes().length;
257 if (length > MAX_SHORT_BLOB_PROPERTY_LENGTH) {
258 throw new IllegalArgumentException(prefix + "byte[] properties must be "
259 + MAX_SHORT_BLOB_PROPERTY_LENGTH
260 + " bytes or less. Instead, use " + Blob.class.getName() + ", which can store binary "
261 + "data of any size.");
267 * Returns true if and only if the supplied {@code Class} can be
268 * stored in the data store.
270 public static boolean isSupportedType(Class<?> clazz) {
271 return SUPPORTED_TYPES.contains(clazz);
275 * Returns an unmodifiable {@code Set} of supported {@code Class}
276 * objects.
278 public static Set<Class<?>> getSupportedTypes() {
279 return Collections.unmodifiableSet(SUPPORTED_TYPES);
282 private DataTypeUtils() {