Revision created by MOE tool push_codebase.
[gae.git] / java / src / main / com / google / appengine / api / search / checkers / FieldChecker.java
blob2837cf2b9ed8aa85c000f6d2fa1eb52b4ceeec91
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.apphosting.api.AppEngineInternal;
8 import com.google.apphosting.api.search.DocumentPb;
9 import com.google.common.base.Strings;
11 import org.antlr.runtime.RecognitionException;
13 import java.nio.charset.Charset;
14 import java.util.Date;
15 import java.util.Locale;
17 /**
18 * Provides checks for Field names, language code, and values: text, HTML, atom
19 * or date.
22 @AppEngineInternal
23 public final class FieldChecker {
25 /**
26 * Checks whether a field name is valid. The field name length must be
27 * between 1 and {@link #MAXIMUM_NAME_LENGTH} and it should match
28 * {@link #FIELD_NAME_PATTERN}.
30 * @param name the field name to check
31 * @return the checked field name
32 * @throws IllegalArgumentException if the field name is null or empty
33 * or is longer than {@literal Field.MAXIMUM_NAME_LENGTH} or it doesn't
34 * match {@literal #FIELD_NAME_PATTERN}.
36 public static String checkFieldName(String name) {
37 return checkFieldName(name, "field name");
40 /**
41 * Checks whether a field name is valid. The field name length must be
42 * between 1 and {@link #MAXIMUM_NAME_LENGTH} and it should match
43 * {@link #FIELD_NAME_PATTERN}.
45 * @param name the field name to check
46 * @param fieldName the name of the Java field name of the class where
47 * name is checked
48 * @return the checked field name
49 * @throws IllegalArgumentException if the field name is null or empty
50 * or is longer than {@literal Field.MAXIMUM_NAME_LENGTH} or it doesn't
51 * match {@literal #FIELD_NAME_PATTERN}.
53 public static String checkFieldName(String name, String fieldName) {
54 Preconditions.checkArgument(name != null, "%s cannot be null", fieldName);
55 Preconditions.checkArgument(!Strings.isNullOrEmpty(name), "%s cannot be null or empty",
56 fieldName);
57 Preconditions.checkArgument(bytesInString(name) <= SearchApiLimits.MAXIMUM_NAME_LENGTH,
58 "%s longer than %d : %s", fieldName, SearchApiLimits.MAXIMUM_NAME_LENGTH, name);
59 Preconditions.checkArgument(name.matches(SearchApiLimits.FIELD_NAME_PATTERN),
60 "%s should match pattern %s: %s", fieldName, SearchApiLimits.FIELD_NAME_PATTERN, name);
61 return name;
64 /**
65 * @return The number of bytes in the given string when UTF-8 encoded
67 static int bytesInString(String str) {
68 return str.getBytes(Charset.forName("UTF-8")).length;
71 /**
72 * @return true if name matches {@link #FIELD_NAME_PATTERN}.
74 static boolean nameMatchesPattern(String name) {
75 return name.matches(SearchApiLimits.FIELD_NAME_PATTERN);
78 /**
79 * Checks whether a text is valid. A text can be null, or a string between
80 * 0 and {@literal Field.MAXIMUM_TEXT_LENGTH} in length.
82 * @param text the text to check
83 * @return the checked text
84 * @throws IllegalArgumentException if text is too long
86 public static String checkText(String text) {
87 if (text != null) {
88 Preconditions.checkArgument(bytesInString(text) <= SearchApiLimits.MAXIMUM_TEXT_LENGTH,
89 "Field text longer than maximum length %d", SearchApiLimits.MAXIMUM_TEXT_LENGTH);
91 return text;
94 /**
95 * Checks whether a html is valid. A html can be null or a string between
96 * 0 and {@literal Field.MAXIMUM_TEXT_LENGTH} in length.
98 * @param html the html to check
99 * @return the checked html
100 * @throws IllegalArgumentException if html is too long
102 public static String checkHTML(String html) {
103 if (html != null) {
104 Preconditions.checkArgument(bytesInString(html) <= SearchApiLimits.MAXIMUM_TEXT_LENGTH,
105 "html longer than maximum length %d", SearchApiLimits.MAXIMUM_TEXT_LENGTH);
107 return html;
111 * Checks whether an atom is valid. An atom can be null or a string between
112 * 1 and {@literal Field.MAXIMUM_ATOM_LENGTH} in length.
114 * @param atom the atom to check
115 * @return the checked atom
116 * @throws IllegalArgumentException if atom is too long
118 public static String checkAtom(String atom) {
119 if (atom != null) {
120 Preconditions.checkArgument(bytesInString(atom) <= SearchApiLimits.MAXIMUM_ATOM_LENGTH,
121 "Field atom longer than maximum length %d", SearchApiLimits.MAXIMUM_ATOM_LENGTH);
123 return atom;
127 * Checks whether a number is valid. A number can be null or a value between
128 * {@link #MIN_NUMBER_VALUE} and {@link #MAX_NUMBER_VALUE}.
130 * @param value the value to check
131 * @return the checked number
132 * @throws IllegalArgumentException if number is too long
134 public static Double checkNumber(Double value) {
135 if (value != null) {
136 Preconditions.checkArgument(SearchApiLimits.MINIMUM_NUMBER_VALUE <= value,
137 String.format("number value, %f, must be greater than or equal to %f",
138 value, SearchApiLimits.MINIMUM_NUMBER_VALUE));
139 Preconditions.checkArgument(value <= SearchApiLimits.MAXIMUM_NUMBER_VALUE,
140 String.format("number value, %f, must be less than or equal to %f",
141 value, SearchApiLimits.MAXIMUM_NUMBER_VALUE));
143 return value;
147 * Checks whether a date is within range. Date is nullable.
149 * @param date the date to check
150 * @return the checked date
151 * @throws IllegalArgumentException if date is out of range
153 public static Date checkDate(Date date) throws IllegalArgumentException {
154 if (date != null) {
155 Preconditions.checkArgument(
156 SearchApiLimits.MINIMUM_DATE_VALUE.compareTo(date) <= 0,
157 String.format("date %s must be after %s",
158 DateUtil.formatDateTime(date),
159 DateUtil.formatDateTime(SearchApiLimits.MINIMUM_DATE_VALUE)));
160 Preconditions.checkArgument(
161 date.compareTo(SearchApiLimits.MAXIMUM_DATE_VALUE) <= 0,
162 String.format("date %s must be before %s",
163 DateUtil.formatDateTime(date),
164 DateUtil.formatDateTime(SearchApiLimits.MAXIMUM_DATE_VALUE)));
166 return date;
170 * Checks whether expression is not null and is parsable.
172 * @param expression the expression to check
173 * @return the checked expression
174 * @throws IllegalArgumentException if the expression is null, or
175 * cannot be parsed
177 public static String checkExpression(String expression) {
178 Preconditions.checkNotNull(expression, "expression cannot be null");
179 try {
180 new ExpressionTreeBuilder().parse(expression);
181 } catch (RecognitionException e) {
182 throw new IllegalArgumentException("Unable to parse expression: " + expression);
184 return expression;
187 public static DocumentPb.Field checkValid(DocumentPb.Field field) {
188 checkFieldName(field.getName());
189 DocumentPb.FieldValue value = field.getValue();
190 switch (value.getType()) {
191 case TEXT:
192 checkText(value.getStringValue());
193 break;
194 case HTML:
195 checkHTML(value.getStringValue());
196 break;
197 case DATE:
198 checkDate(DateUtil.deserializeDate(value.getStringValue()));
199 break;
200 case ATOM:
201 checkAtom(value.getStringValue());
202 break;
203 case NUMBER:
204 checkNumber(Double.parseDouble(value.getStringValue()));
205 break;
206 case GEO:
207 GeoPointChecker.checkValid(value.getGeo());
208 break;
209 default:
210 throw new IllegalArgumentException("Unsupported field type " + value.getType());
212 return field;
216 * Returns a {@link Locale} parsed from the given locale string.
218 * @param locale a string representation of a {@link Locale}
219 * @return a {@link Locale} parsed from the given locale string
220 * @throws IllegalArgumentException if the locale cannot be parsed
222 public static Locale parseLocale(String locale) {
223 if (locale == null) {
224 return null;
226 String[] parts = locale.split("_", 3);
227 if (parts.length == 1) {
228 return new Locale(parts[0]);
230 if (parts.length == 2) {
231 return new Locale(parts[0], parts[1]);
233 if (parts.length == 3) {
234 return new Locale(parts[0], parts[1], parts[2]);
236 throw new IllegalArgumentException("Cannot parse locale " + locale);