1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.myfaces.trinidad.validator;
20
21 import java.io.UnsupportedEncodingException;
22
23 import java.nio.charset.IllegalCharsetNameException;
24
25 import javax.el.ValueExpression;
26
27 import javax.faces.application.FacesMessage;
28 import javax.faces.component.StateHolder;
29 import javax.faces.component.UIComponent;
30 import javax.faces.context.FacesContext;
31 import javax.faces.el.ValueBinding;
32 import javax.faces.validator.Validator;
33 import javax.faces.validator.ValidatorException;
34
35 import org.apache.myfaces.trinidad.bean.FacesBean;
36 import org.apache.myfaces.trinidad.bean.PropertyKey;
37 import org.apache.myfaces.trinidad.util.ComponentUtils;
38 import org.apache.myfaces.trinidad.util.MessageFactory;
39 import org.apache.myfaces.trinidad.logging.TrinidadLogger;
40
41
42 /**
43 * Validator for validating the byte length of strings.
44 * If encoding is not specified, defaults to ISO-8859-1
45 */
46 /**
47 * <p><strong>ByteLengthValidator</strong> is a {@link Validator} that checks
48 * the value of the corresponding component for its byte length for the set
49 * character encoding. The following algorithm is implemented:</p>
50 * <ul>
51 * <li>If the passed value is <code>null</code>, exit immediately.</li>
52 * <li>If the current component value is not of String type throw
53 * IllegalArgumentException
54 * <li>If both <code>encoding</code> and <code>maximum</code> property
55 * has been configured on this {@link Validator}, check the component
56 * value byte length against the maximum. If the component value byte length
57 * is greater than this specified maximum, throw a {@link ValidatorException}
58 * containing a MAXIMUM_MESSAGE_ID message.</li>
59 * <li>If only <code>maximum</code> property has been configured on this
60 * {@link Validator}, check the component value against
61 * this limit defaulting the encoding to be <code>iso-8859-1</code>.
62 * If the component value length is greater than the specified maximum,
63 * throw a {@link ValidatorException} containing a MAXIMUM_MESSAGE_ID
64 * message.</li>
65 * <li>If a <code>encoding</code> property has been configured on this
66 * {@link Validator}, and if it is not a valid Java encoding, then throws a
67 * {@link java.nio.charset.IllegalCharsetNameException}</li>
68 *
69 * <li>If <code>maximumMessageDetail</code> is set, it is used for constructing
70 * faces message, when validation fails. The message can contain placeholders
71 * which will be replaced as specified in {@link #MAXIMUM_MESSAGE_ID}
72 * </li>
73 * </ul>
74 * @see #setMessageDetailMaximum(String)
75 *
76 * @version $Name: $ ($Revision: adfrt/faces/adf-faces-api/src/main/java/oracle/adf/view/faces/validator/ByteLengthValidator.java#0 $) $Date: 10-nov-2005.19:08:32 $
77 */
78 public class ByteLengthValidator implements StateHolder, Validator
79 {
80
81 /**
82 * <p>The message identifier of the {@link FacesMessage} to be created if
83 * the maximum byte length check fails. The message format string for this
84 * message may optionally include <code>{0}</code>, <code>{1}</code> and
85 * <code>{2}</code> placeholders, which will be replaced by input value, label
86 * associated with the component and the maximum bytes respectively.<p>
87 */
88 public static final String MAXIMUM_MESSAGE_ID =
89 "org.apache.myfaces.trinidad.validator.ByteLengthValidator.MAXIMUM";
90
91 /**
92 * <p>Standard validator id for this validator.</p>
93 */
94 public static final String VALIDATOR_ID = "org.apache.myfaces.trinidad.ByteLength";
95
96
97 /**
98 * <p>Construct a {@link Validator} with <code>iso-8859-1</code> as the encoding
99 * and <code>zero</code> as the maximum bytes allowed.</p>
100 */
101 public ByteLengthValidator()
102 {
103 setEncoding("iso-8859-1");
104 }
105
106 /**
107 * <p>Construct a {@link Validator} with the specified preconfigured
108 * values.</p>
109 * @param maximum the maximum number of bytes allowed.
110 * @param encoding the Java character set encoding. This must be
111 * an encoding supported by Java.
112 */
113 public ByteLengthValidator(int maximum, String encoding)
114 {
115 super();
116 setMaximum(maximum);
117 setEncoding(encoding);
118 }
119
120 /**
121 * <p>Set the character encoding for this {@link Validator}.</p>
122 *
123 * @param encoding The character encoding.
124 */
125 public void setEncoding(String encoding)
126 {
127 _facesBean.setProperty(_ENCODING_KEY, encoding);
128 }
129
130 /**
131 * <p>Return the character encoding set for this {@link Validator} or
132 * <code>iso-8859-1</code> if it has not been set.</p>
133 */
134 public String getEncoding()
135 {
136 Object encoding = _facesBean.getProperty(_ENCODING_KEY);
137 return ComponentUtils.resolveString(encoding);
138 }
139
140 /**
141 * <p>Set the maximum bytes to be enforced by this {@link Validator}.</p>
142 *
143 * @param maximum The new maximum value
144 *
145 */
146 public void setMaximum(int maximum)
147 {
148 _facesBean.setProperty(_MAXIMUM_KEY, Integer.valueOf(maximum));
149 }
150
151 /**
152 * <p>Return the maximum bytes to be enforced by this {@link
153 * Validator} or <code>zero</code> if it has not been
154 * set.</p>
155 */
156 public int getMaximum()
157 {
158 return ComponentUtils.resolveInteger(_facesBean.getProperty(_MAXIMUM_KEY));
159 }
160
161 /**
162 * <p>Custom error message to be used, for creating detail part of the
163 * {@link FacesMessage}, when users input exceeds the maximum byte length.</p>
164 * Overrides detail message identified by message id {@link #MAXIMUM_MESSAGE_ID}
165 * @param maximumMessageDetail Custom error message.
166 */
167 public void setMessageDetailMaximum(String maximumMessageDetail)
168 {
169 _facesBean.setProperty(_MAXIMUM_MESSAGE_DETAIL_KEY, maximumMessageDetail);
170 }
171
172 /**
173 * <p>Return custom detail error message that was set for creating {@link FacesMessage},
174 * for values that exceeds the maximum byte length.</p>
175 * @return Custom error message.
176 * @see #setMessageDetailMaximum(String)
177 */
178 public String getMessageDetailMaximum()
179 {
180 Object obj = _facesBean.getProperty(_MAXIMUM_MESSAGE_DETAIL_KEY);
181 return ComponentUtils.resolveString(obj);
182 }
183
184 /**
185 * <p>Custom hint message.</p>
186 * Overrides default hint message
187 * @param hintMaximum Custom hint message.
188 */
189 public void setHintMaximum(String hintMaximum)
190 {
191 _facesBean.setProperty(_HINT_MAXIMUM_KEY, hintMaximum);
192 }
193
194 /**
195 * <p>Return custom hint message.</p>
196 * @return Custom hint message.
197 * @see #setHintMaximum(String)
198 */
199 public String getHintMaximum()
200 {
201 Object obj = _facesBean.getProperty(_HINT_MAXIMUM_KEY);
202 return ComponentUtils.resolveString(obj);
203 }
204
205 /**
206 * <p>Validates unless it is too long, in which case throws
207 * ValidatorException.</p>
208 * @exception ValidatorException if validation fails
209 * @exception NullPointerException if <code>context</code>
210 * @exception IllegalCharsetNameException if <code>encoding</code> is
211 * @exception IllegalArgumentException if <code>value</code> is not of type
212 * {@link java.lang.String}
213 * <code>unsupported</code>
214 * or <code>component</code> is <code>null</code>
215 */
216 public void validate(
217 FacesContext context,
218 UIComponent component,
219 Object value
220 ) throws ValidatorException
221 {
222
223 if ((context == null) || (component == null))
224 {
225 throw new NullPointerException(_LOG.getMessage(
226 "NULL_FACESCONTEXT_OR_UICOMPONENT"));
227 }
228
229 if (value != null)
230 {
231 ValidatorUtils.assertIsString(value,
232 "'value' is not of type java.lang.String.");
233 String theValue = (String)value;
234
235 int maxBytes = getMaximum();
236 try
237 {
238 byte[] bytes = theValue.getBytes(getEncoding());
239 if (bytes.length > maxBytes)
240 throw new ValidatorException(
241 getLengthValidationFailureMessage(context, component, theValue));
242
243 }
244 catch (UnsupportedEncodingException uee)
245 {
246 throw new IllegalCharsetNameException(_LOG.getMessage(
247 "ENCODING_NOT_SUPPORTED_BY_JVM", getEncoding()));
248 }
249 }
250 }
251
252 public Object saveState(FacesContext context)
253 {
254 return _facesBean.saveState(context);
255 }
256
257 public void restoreState(FacesContext context, Object state)
258 {
259 _facesBean.restoreState(context, state);
260 }
261
262 public boolean isTransient()
263 {
264 return (_isTransient);
265 }
266
267 public void setTransient(boolean transientValue)
268 {
269 _isTransient = transientValue;
270 }
271
272
273 /**
274 * <p>Set the {@link ValueExpression} used to calculate the value for the
275 * specified attribute if any.</p>
276 *
277 * @param name Name of the attribute for which to set a {@link ValueExpression}
278 * @param expression The {@link ValueExpression} to set, or <code>null</code>
279 * to remove any currently set {@link ValueExpression}
280 *
281 * @exception NullPointerException if <code>name</code>
282 * is <code>null</code>
283 * @exception IllegalArgumentException if <code>name</code> is not a valid
284 * attribute of this converter
285 */
286 public void setValueExpression(String name, ValueExpression expression)
287 {
288 ValidatorUtils.setValueExpression(_facesBean, name, expression) ;
289 }
290
291
292 /**
293 * <p>Return the {@link ValueExpression} used to calculate the value for the
294 * specified attribute name, if any.</p>
295 *
296 * @param name Name of the attribute or property for which to retrieve a
297 * {@link ValueExpression}
298 *
299 * @exception NullPointerException if <code>name</code>
300 * is <code>null</code>
301 * @exception IllegalArgumentException if <code>name</code> is not a valid
302 * attribute of this converter
303 */
304 public ValueExpression getValueExpression(String name)
305 {
306 return ValidatorUtils.getValueExpression(_facesBean, name);
307 }
308
309 /**
310 * <p>Set the {@link ValueBinding} used to calculate the value for the
311 * specified attribute if any.</p>
312 *
313 * @param name Name of the attribute for which to set a {@link ValueBinding}
314 * @param binding The {@link ValueBinding} to set, or <code>null</code>
315 * to remove any currently set {@link ValueBinding}
316 *
317 * @exception NullPointerException if <code>name</code>
318 * is <code>null</code>
319 * @exception IllegalArgumentException if <code>name</code> is not a valid
320 * attribute of this validator
321 * @deprecated
322 */
323 public void setValueBinding(String name, ValueBinding binding)
324 {
325 ValidatorUtils.setValueBinding(_facesBean, name, binding) ;
326 }
327
328 /**
329 * <p>Return the {@link ValueBinding} used to calculate the value for the
330 * specified attribute name, if any.</p>
331 *
332 * @param name Name of the attribute or property for which to retrieve a
333 * {@link ValueBinding}
334 *
335 * @exception NullPointerException if <code>name</code>
336 * is <code>null</code>
337 * @exception IllegalArgumentException if <code>name</code> is not a valid
338 * attribute of this validator
339 * @deprecated
340 */
341 public ValueBinding getValueBinding(String name)
342 {
343 return ValidatorUtils.getValueBinding(_facesBean, name);
344 }
345
346 /**
347 * <p>Compares this ByteLengthValidator with the specified Object for
348 * equality.</p>
349 * @param object Object to which this ByteLengthValidator is to be compared.
350 * @return true if and only if the specified Object is a ByteLengthValidator
351 * and if the values encoding, maximum and transient are equal.
352 */
353 @Override
354 public boolean equals(Object object)
355 {
356
357 if (this == object)
358 return true;
359
360 if ( object instanceof ByteLengthValidator )
361 {
362 ByteLengthValidator other = (ByteLengthValidator) object;
363 String encoding = getEncoding();
364 String otherEncoding = other.getEncoding();
365 String otherMsgMaxDet = other.getMessageDetailMaximum();
366 String msgMaxDet = getMessageDetailMaximum();
367
368 if ( this.isTransient() == other.isTransient() &&
369 ValidatorUtils.equals(encoding, otherEncoding) &&
370 ValidatorUtils.equals(msgMaxDet, otherMsgMaxDet) &&
371 (getMaximum() == other.getMaximum())
372 )
373 {
374 return true;
375 }
376 }
377 return false;
378 }
379
380 /**
381 * <p>Returns the hash code for this Validator.</p>
382 * @return a hash code value for this object.
383 */
384 @Override
385 public int hashCode()
386 {
387 int result = 17;
388 String maximumMsgDet = getMessageDetailMaximum();
389 String encoding = getEncoding();
390 result = 37 * result + (encoding == null? 0 : encoding.hashCode());
391 result = 37 * result + (_isTransient ? 0 : 1);
392 result = 37 * result + getMaximum();
393 result = 37 * result + (maximumMsgDet == null? 0 : maximumMsgDet.hashCode());
394 return result;
395 }
396
397 /**
398 * The {@link FacesMessage} to be returned if byte length validation fails.
399 * @param context Faces context
400 * @param value The value entered / set by the user on the component
401 * @return error message when the length exceeds the maximum byte length set.
402 */
403 protected FacesMessage getLengthValidationFailureMessage(
404 FacesContext context,
405 UIComponent component,
406 String value
407 )
408 {
409 Object label = ValidatorUtils.getComponentLabel(component);
410
411 Object maxMesgDetail = _getRawMaximumMessageDetail();
412 String maximumBytes = String.valueOf(getMaximum());
413
414 Object[] params = { label, value, maximumBytes};
415
416 FacesMessage msg = MessageFactory.getMessage(context,
417 MAXIMUM_MESSAGE_ID,
418 maxMesgDetail,
419 params,
420 component);
421 return msg;
422 }
423
424 private Object _getRawMaximumMessageDetail()
425 {
426 return _facesBean.getRawProperty(_MAXIMUM_MESSAGE_DETAIL_KEY);
427 }
428
429 private static final FacesBean.Type _TYPE = new FacesBean.Type();
430
431 private static final PropertyKey _ENCODING_KEY =
432 _TYPE.registerKey("encoding", String.class, "iso-8859-1");
433
434 private static final PropertyKey _MAXIMUM_KEY =
435 _TYPE.registerKey("maximumBytes", int.class, 0);
436
437 private static final PropertyKey _MAXIMUM_MESSAGE_DETAIL_KEY =
438 _TYPE.registerKey("messageDetailMaximum", String.class);
439
440 private static final PropertyKey _HINT_MAXIMUM_KEY =
441 _TYPE.registerKey("hintMaximum", String.class);
442
443 private FacesBean _facesBean = ValidatorUtils.getFacesBean(_TYPE);
444
445 private boolean _isTransient = false;
446
447
448 private static final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(
449 ByteLengthValidator.class);
450 }