Version 1.7.4
[gae.git] / java / src / main / com / google / appengine / api / search / checkers / FieldChecker.java
bloba978121b2bb4c89dad55d482d61e10024ba24722
1 // Copyright 2010 Google Inc. All Rights Reserved.
3 package com.google.appengine.api.search.checkers;
5 import com.google.appengine.api.search.DateUtil;
6 import com.google.appengine.api.search.ExpressionTreeBuilder;
7 import com.google.appengine.api.search.Util;
8 import com.google.apphosting.api.AppEngineInternal;
9 import com.google.apphosting.api.search.DocumentPb;
11 import org.antlr.runtime.RecognitionException;
13 import java.nio.charset.Charset;
15 import java.util.Date;
16 import java.util.Locale;
18 /**
19 * Provides checks for Field names, language code, and values: text, HTML, atom
20 * or date.
23 @AppEngineInternal
24 public final class FieldChecker {
26 /**
27 * The maximum length of a field name in bytes ({@value}).
28 * @deprecated From 1.7.4, use {@link SearchApiLimits#MAXIMUM_NAME_LENGTH}
30 @Deprecated public static final int MAXIMUM_NAME_LENGTH = 500;
32 /**
33 * The maximum length of a text or HTML in bytes ({@value}).
34 * @deprecated From 1.7.4, use {@link SearchApiLimits#MAXIMUM_TEXT_LENGTH}
36 @Deprecated public static final int MAXIMUM_TEXT_LENGTH = 1024 * 1024;
38 /**
39 * The maximum length of an atom in bytes ({@value}).
40 * @deprecated From 1.7.4, use {@link SearchApiLimits#MAXIMUM_ATOM_LENGTH}
42 @Deprecated public static final int MAXIMUM_ATOM_LENGTH = 500;
44 /**
45 * The maximum value that can be stored in a number field ({@value}).
46 * @deprecated From 1.7.4, use {@link SearchApiLimits#MAXIMUM_NUMBER_VALUE}
48 @Deprecated public static final float MAX_NUMBER_VALUE = 2147483647;
50 /**
51 * The minimum value that can be stored in a number field ({@value}).
52 * @deprecated From 1.7.4, use {@link SearchApiLimits#MINIMUM_NUMBER_VALUE}
54 @Deprecated public static final float MIN_NUMBER_VALUE = -2147483647;
56 /**
57 * The pattern each document field name should match.
58 * @deprecated From 1.7.4, use {@link SearchApiLimits#FIELD_NAME_PATTERN}
60 @Deprecated public static final String FIELD_NAME_PATTERN = "^[A-Za-z][A-Za-z0-9_]*$";
62 /**
63 * The maximum date that can be stored in a date field.
64 * @deprecated From 1.7.4, use {@link SearchApiLimits#MAXIMUM_DATE_VALUE}
66 @Deprecated public static final Date MAX_DATE =
67 DateUtil.getEpochPlusDays(
68 Integer.MAX_VALUE, DateUtil.MILLISECONDS_IN_DAY - 1);
70 /**
71 * The minimum date that can be stored in a date field.
72 * @deprecated From 1.7.4, use {@link SearchApiLimits#MINIMUM_DATE_VALUE}
74 @Deprecated public static final Date MIN_DATE =
75 DateUtil.getEpochPlusDays(Integer.MIN_VALUE, 0);
77 /**
78 * Checks whether a field name is valid. The field name length must be
79 * between 1 and {@link #MAXIMUM_NAME_LENGTH} and it should match
80 * {@link #FIELD_NAME_PATTERN}.
82 * @param name the field name to check
83 * @return the checked field name
84 * @throws IllegalArgumentException if the field name is null or empty
85 * or is longer than {@literal Field.MAXIMUM_NAME_LENGTH} or it doesn't
86 * match {@literal #FIELD_NAME_PATTERN}.
88 public static String checkFieldName(String name) {
89 return checkFieldName(name, "field name");
92 /**
93 * Checks whether a field name is valid. The field name length must be
94 * between 1 and {@link #MAXIMUM_NAME_LENGTH} and it should match
95 * {@link #FIELD_NAME_PATTERN}.
97 * @param name the field name to check
98 * @param fieldName the name of the Java field name of the class where
99 * name is checked
100 * @return the checked field name
101 * @throws IllegalArgumentException if the field name is null or empty
102 * or is longer than {@literal Field.MAXIMUM_NAME_LENGTH} or it doesn't
103 * match {@literal #FIELD_NAME_PATTERN}.
105 public static String checkFieldName(String name, String fieldName) {
106 Preconditions.checkArgument(name != null, "%s cannot be null", fieldName);
107 Preconditions.checkArgument(!Util.isNullOrEmpty(name), "%s cannot be null or empty",
108 fieldName);
109 Preconditions.checkArgument(bytesInString(name) <= SearchApiLimits.MAXIMUM_NAME_LENGTH,
110 "%s longer than %d : %s", fieldName, SearchApiLimits.MAXIMUM_NAME_LENGTH, name);
111 Preconditions.checkArgument(name.matches(SearchApiLimits.FIELD_NAME_PATTERN),
112 "%s should match pattern %s: %s", fieldName, SearchApiLimits.FIELD_NAME_PATTERN, name);
113 return name;
117 * @return The number of bytes in the given string when UTF-8 encoded
119 static int bytesInString(String str) {
120 return str.getBytes(Charset.forName("UTF-8")).length;
124 * @return true if name matches {@link #FIELD_NAME_PATTERN}.
126 static boolean nameMatchesPattern(String name) {
127 return name.matches(SearchApiLimits.FIELD_NAME_PATTERN);
131 * Checks whether a text is valid. A text can be null, or a string between
132 * 0 and {@literal Field.MAXIMUM_TEXT_LENGTH} in length.
134 * @param text the text to check
135 * @return the checked text
136 * @throws IllegalArgumentException if text is too long
138 public static String checkText(String text) {
139 if (text != null) {
140 Preconditions.checkArgument(bytesInString(text) <= SearchApiLimits.MAXIMUM_TEXT_LENGTH,
141 "Field text longer than maximum length %d", SearchApiLimits.MAXIMUM_TEXT_LENGTH);
143 return text;
147 * Checks whether a html is valid. A html can be null or a string between
148 * 0 and {@literal Field.MAXIMUM_TEXT_LENGTH} in length.
150 * @param html the html to check
151 * @return the checked html
152 * @throws IllegalArgumentException if html is too long
154 public static String checkHTML(String html) {
155 if (html != null) {
156 Preconditions.checkArgument(bytesInString(html) <= SearchApiLimits.MAXIMUM_TEXT_LENGTH,
157 "html longer than maximum length %d", SearchApiLimits.MAXIMUM_TEXT_LENGTH);
159 return html;
163 * Checks whether an atom is valid. An atom can be null or a string between
164 * 1 and {@literal Field.MAXIMUM_ATOM_LENGTH} in length.
166 * @param atom the atom to check
167 * @return the checked atom
168 * @throws IllegalArgumentException if atom is too long
170 public static String checkAtom(String atom) {
171 if (atom != null) {
172 Preconditions.checkArgument(bytesInString(atom) <= SearchApiLimits.MAXIMUM_ATOM_LENGTH,
173 "Field atom longer than maximum length %d", SearchApiLimits.MAXIMUM_ATOM_LENGTH);
175 return atom;
179 * Checks whether a number is valid. A number can be null or a value between
180 * {@link #MIN_NUMBER_VALUE} and {@link #MAX_NUMBER_VALUE}.
182 * @param value the value to check
183 * @return the checked number
184 * @throws IllegalArgumentException if number is too long
186 public static Double checkNumber(Double value) {
187 if (value != null) {
188 Preconditions.checkArgument(SearchApiLimits.MINIMUM_NUMBER_VALUE <= value,
189 String.format("number value, %f, must be greater than or equal to %f",
190 value, SearchApiLimits.MINIMUM_NUMBER_VALUE));
191 Preconditions.checkArgument(value <= SearchApiLimits.MAXIMUM_NUMBER_VALUE,
192 String.format("number value, %f, must be less than or equal to %f",
193 value, SearchApiLimits.MAXIMUM_NUMBER_VALUE));
195 return value;
199 * Checks whether a date is within range. Date is nullable.
201 * @param date the date to check
202 * @return the checked date
203 * @throws IllegalArgumentException if date is out of range
205 public static Date checkDate(Date date) throws IllegalArgumentException {
206 if (date != null) {
207 Preconditions.checkArgument(
208 SearchApiLimits.MINIMUM_DATE_VALUE.compareTo(date) <= 0,
209 String.format("date %s must be after %s",
210 DateUtil.formatDateTime(date),
211 DateUtil.formatDateTime(SearchApiLimits.MINIMUM_DATE_VALUE)));
212 Preconditions.checkArgument(
213 date.compareTo(SearchApiLimits.MAXIMUM_DATE_VALUE) <= 0,
214 String.format("date %s must be before %s",
215 DateUtil.formatDateTime(date),
216 DateUtil.formatDateTime(SearchApiLimits.MAXIMUM_DATE_VALUE)));
218 return date;
222 * Checks whether expression is not null and is parsable.
224 * @param expression the expression to check
225 * @return the checked expression
226 * @throws IllegalArgumentException if the expression is null, or
227 * cannot be parsed
229 public static String checkExpression(String expression) {
230 Preconditions.checkNotNull(expression, "expression cannot be null");
231 try {
232 new ExpressionTreeBuilder().parse(expression);
233 } catch (RecognitionException e) {
234 throw new IllegalArgumentException("Unable to parse expression: " + expression);
236 return expression;
239 public static DocumentPb.Field checkValid(DocumentPb.Field field) {
240 checkFieldName(field.getName());
241 DocumentPb.FieldValue value = field.getValue();
242 switch (value.getType()) {
243 case TEXT:
244 checkText(value.getStringValue());
245 break;
246 case HTML:
247 checkHTML(value.getStringValue());
248 break;
249 case DATE:
250 checkDate(DateUtil.deserializeDate(value.getStringValue()));
251 break;
252 case ATOM:
253 checkAtom(value.getStringValue());
254 break;
255 case NUMBER:
256 checkNumber(Double.parseDouble(value.getStringValue()));
257 break;
258 case GEO:
259 GeoPointChecker.checkValid(value.getGeo());
260 break;
261 default:
262 throw new IllegalArgumentException("Unsupported field type " + value.getType());
264 return field;
268 * Returns a {@link Locale} parsed from the given locale string.
270 * @param locale a string representation of a {@link Locale}
271 * @return a {@link Locale} parsed from the given locale string
272 * @throws IllegalArgumentException if the locale cannot be parsed
274 public static Locale parseLocale(String locale) {
275 if (locale == null) {
276 return null;
278 String[] parts = locale.split("_", 3);
279 if (parts.length == 1) {
280 return new Locale(parts[0]);
282 if (parts.length == 2) {
283 return new Locale(parts[0], parts[1]);
285 if (parts.length == 3) {
286 return new Locale(parts[0], parts[1], parts[2]);
288 throw new IllegalArgumentException("Cannot parse locale " + locale);