1 /*
2 * $Id: UIInput.java,v 1.93.4.2 2007/11/05 21:48:57 rlubke Exp $
3 */
4
5 /*
6 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
7 *
8 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
9 *
10 * The contents of this file are subject to the terms of either the GNU
11 * General Public License Version 2 only ("GPL") or the Common Development
12 * and Distribution License("CDDL") (collectively, the "License"). You
13 * may not use this file except in compliance with the License. You can obtain
14 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
15 * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
16 * language governing permissions and limitations under the License.
17 *
18 * When distributing the software, include this License Header Notice in each
19 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
20 * Sun designates this particular file as subject to the "Classpath" exception
21 * as provided by Sun in the GPL Version 2 section of the License file that
22 * accompanied this code. If applicable, add the following below the License
23 * Header, with the fields enclosed by brackets [] replaced by your own
24 * identifying information: "Portions Copyrighted [year]
25 * [name of copyright owner]"
26 *
27 * Contributor(s):
28 *
29 * If you wish your version of this file to be governed by only the CDDL or
30 * only the GPL Version 2, indicate your decision by adding "[Contributor]
31 * elects to include this software in this distribution under the [CDDL or GPL
32 * Version 2] license." If you don't indicate a single choice of license, a
33 * recipient has the option to distribute your version of this file under
34 * either the CDDL, the GPL Version 2 or to extend the choice of license to
35 * its licensees as provided above. However, if you add GPL Version 2 code
36 * and therefore, elected the GPL Version 2 license, then the option applies
37 * only if the new code is made subject to such option by the copyright
38 * holder.
39 */
40
41 package javax.faces.component;
42
43 import java.util.ArrayList;
44 import java.util.Iterator;
45 import java.util.List;
46 import java.util.logging.Level;
47 import java.util.logging.Logger;
48
49 import javax.el.ELException;
50 import javax.el.ValueExpression;
51 import javax.faces.FacesException;
52 import javax.faces.application.Application;
53 import javax.faces.application.FacesMessage;
54 import javax.faces.context.FacesContext;
55 import javax.faces.convert.Converter;
56 import javax.faces.convert.ConverterException;
57 import javax.faces.el.MethodBinding;
58 import javax.faces.event.ValueChangeEvent;
59 import javax.faces.event.ValueChangeListener;
60 import javax.faces.render.Renderer;
61 import javax.faces.validator.Validator;
62 import javax.faces.validator.ValidatorException;
63
64 /**
65 * <p><strong>UIInput</strong> is a {@link UIComponent} that represents
66 * a component that both displays output to the user (like
67 * {@link UIOutput} components do) and processes request parameters on the
68 * subsequent request that need to be decoded. There are no restrictions
69 * on the data type of the local value, or the object referenced by the
70 * value binding expression (if any); however, individual
71 * {@link javax.faces.render.Renderer}s will generally impose restrictions
72 * on the type of data they know how to display.</p>
73 * <p/>
74 * <p>During the <em>Apply Request Values</em> phase of the request
75 * processing lifecycle, the decoded value of this component, usually
76 * but not necessarily a String, must be stored - but not yet converted -
77 * using <code>setSubmittedValue()</code>. If the component wishes
78 * to indicate that no particular value was submitted, it can either
79 * do nothing, or set the submitted value to <code>null</code>.</p>
80 * <p></p>
81 * <p>By default, during the <em>Process Validators</em> phase of the
82 * request processing lifecycle, the submitted value will be converted
83 * to a typesafe object, and, if validation succeeds, stored as a
84 * local value using <code>setValue()</code>. However, if the
85 * <code>immediate</code> property is set to <code>true</code>, this
86 * processing will occur instead at the end of the
87 * <em>Apply Request Values</em> phase.
88 * </p>
89 * <p>During the <em>Render Response</em> phase of the request processing
90 * lifecycle, conversion for output occurs as for {@link UIOutput}.</p>
91 * <p/>
92 * <p>When the <code>validate()</code> method of this {@link UIInput}
93 * detects that a value change has actually occurred, and that all validations
94 * have been successfully passed, it will queue a
95 * {@link ValueChangeEvent}. Later on, the <code>broadcast()</code>
96 * method will ensure that this event is broadcast to all interested
97 * listeners. This event will be delivered by default in the
98 * <em>Process Validators</em> phase, but can be delivered instead
99 * during <em>Apply Request Values</em> if the <code>immediate</code>
100 * property is set to <code>true</code>.</p>
101 * <p/>
102 * <p>By default, the <code>rendererType</code> property must be set to
103 * "<code>Text</code>". This value can be changed by calling the
104 * <code>setRendererType()</code> method.</p>
105 */
106
107 public class UIInput extends UIOutput implements EditableValueHolder {
108
109 // ------------------------------------------------------ Manifest Constants
110
111
112 /**
113 * <p>The standard component type for this component.</p>
114 */
115 public static final String COMPONENT_TYPE = "javax.faces.Input";
116
117
118 /**
119 * <p>The standard component family for this component.</p>
120 */
121 public static final String COMPONENT_FAMILY = "javax.faces.Input";
122
123
124 /**
125 * <p>The message identifier of the
126 * {@link javax.faces.application.FacesMessage} to be created if
127 * a conversion error occurs, and neither the page author nor
128 * the {@link ConverterException} provides a message.</p>
129 */
130 public static final String CONVERSION_MESSAGE_ID =
131 "javax.faces.component.UIInput.CONVERSION";
132
133
134 /**
135 * <p>The message identifier of the
136 * {@link javax.faces.application.FacesMessage} to be created if
137 * a required check fails.</p>
138 */
139 public static final String REQUIRED_MESSAGE_ID =
140 "javax.faces.component.UIInput.REQUIRED";
141
142 /**
143 * <p>The message identifier of the
144 * {@link javax.faces.application.FacesMessage} to be created if
145 * a model update error occurs, and the thrown exception has
146 * no message.</p>
147 */
148 public static final String UPDATE_MESSAGE_ID =
149 "javax.faces.component.UIInput.UPDATE";
150 private static final Validator[] EMPTY_VALIDATOR = new Validator[0];
151
152 /**
153 * The <code>Logger</code> for this class.
154 */
155 private static final Logger LOGGER =
156 Logger.getLogger("javax.faces.component", "javax.faces.LogStrings");
157
158 // ------------------------------------------------------------ Constructors
159
160
161 /**
162 * <p>Create a new {@link UIInput} instance with default property
163 * values.</p>
164 */
165 public UIInput() {
166
167 super();
168 setRendererType("javax.faces.Text");
169
170 }
171
172 // -------------------------------------------------------------- Properties
173
174
175 public String getFamily() {
176
177 return (COMPONENT_FAMILY);
178
179 }
180
181
182 /**
183 * <p>The submittedValue value of this {@link UIInput} component.</p>
184 */
185 private Object submittedValue = null;
186
187
188 /**
189 * <p>Return the submittedValue value of this {@link UIInput} component.
190 * This method should only be used by the <code>decode()</code> and
191 * <code>validate()</code> method of this component, or
192 * its corresponding {@link Renderer}.</p>
193 */
194 public Object getSubmittedValue() {
195
196 return (this.submittedValue);
197
198 }
199
200
201 /**
202 * <p>Set the submittedValue value of this {@link UIInput} component.
203 * This method should only be used by the <code>decode()</code> and
204 * <code>validate()</code> method of this component, or
205 * its corresponding {@link Renderer}.</p>
206 *
207 * @param submittedValue The new submitted value
208 */
209 public void setSubmittedValue(Object submittedValue) {
210
211 this.submittedValue = submittedValue;
212
213 }
214
215 public void setValue(Object value) {
216 super.setValue(value);
217 // Mark the local value as set.
218 setLocalValueSet(true);
219 }
220
221 /**
222 * <p>Convenience method to reset this component's value to the
223 * un-initialized state. This method does the following:</p>
224 * <p/>
225 * <p>Call {@link #setValue} passing <code>null</code>.</p>
226 * <p/>
227 * <p>Call {@link #setSubmittedValue} passing <code>null</code>.</p>
228 * <p/>
229 * <p>Call {@link #setLocalValueSet} passing <code>false</code>.</p>
230 * <p/>
231 * <p>Call {@link #setValid} passing <code>true</code>.</p>
232 * <p/>
233 * <p>Upon return from this call if the instance had a
234 * <code>ValueBinding</code> associated with it for the "value"
235 * property, this binding is evaluated when {@link
236 * UIOutput#getValue} is called. Otherwise, <code>null</code> is
237 * returned from <code>getValue()</code>.</p>
238 */
239
240 public void resetValue() {
241 this.setValue(null);
242 this.setSubmittedValue(null);
243 this.setLocalValueSet(false);
244 this.setValid(true);
245 }
246
247 /**
248 * <p>The "localValueSet" state for this component.
249 */
250 private boolean localValueSet;
251
252 /**
253 * Return the "local value set" state for this component.
254 * Calls to <code>setValue()</code> automatically reset
255 * this property to <code>true</code>.
256 */
257 public boolean isLocalValueSet() {
258 return localValueSet;
259 }
260
261 /**
262 * Sets the "local value set" state for this component.
263 */
264 public void setLocalValueSet(boolean localValueSet) {
265 this.localValueSet = localValueSet;
266 }
267
268
269 /**
270 * <p>The "required field" state for this component.</p>
271 */
272 private Boolean required;
273
274 /**
275 * <p>Return the "required field" state for this component.</p>
276 */
277 public boolean isRequired() {
278
279 if (required != null) {
280 return (this.required);
281 }
282 ValueExpression ve = getValueExpression("required");
283 if (ve != null) {
284 try {
285 return (Boolean.TRUE.equals(ve.getValue(getFacesContext().getELContext())));
286 }
287 catch (ELException e) {
288 throw new FacesException(e);
289 }
290 } else {
291 return (false);
292 }
293
294 }
295
296 private String requiredMessage;
297
298 /**
299 * <p>If there has been a call to {@link #setRequiredMessage} on this
300 * instance, return the message. Otherwise, call {@link #getValueExpression}
301 * passing the key "requiredMessage", get the result of the expression, and return it.
302 * Any {@link ELException}s thrown during the call to <code>getValue()</code>
303 * must be wrapped in a {@link FacesException} and rethrown.
304 */
305
306 public String getRequiredMessage() {
307 if (requiredMessage != null) {
308 return requiredMessage;
309 }
310
311 ValueExpression ve = getValueExpression("requiredMessage");
312 if (ve != null) {
313 try {
314 return ((String) ve.getValue(getFacesContext().getELContext()));
315 }
316 catch (ELException e) {
317 throw new FacesException(e);
318 }
319 } else {
320 return (null);
321 }
322
323 }
324
325 /**
326 * <p>Override any {@link ValueExpression} set for the "requiredMessage"
327 * with the literal argument provided to this method. Subsequent calls
328 * to {@link #getRequiredMessage} will return this value;</p>
329 *
330 * @param message the literal message value to be displayed in the event
331 * the user hasn't supplied a value and one is required.
332 */
333
334 public void setRequiredMessage(String message) {
335 requiredMessage = message;
336 }
337
338 private String converterMessage;
339
340 /**
341 * <p>If there has been a call to {@link #setConverterMessage} on this
342 * instance, return the message. Otherwise, call {@link #getValueExpression}
343 * passing the key "converterMessage", get the result of the expression, and return it.
344 * Any {@link ELException}s thrown during the call to <code>getValue()</code>
345 * must be wrapped in a {@link FacesException} and rethrown.
346 */
347
348 public String getConverterMessage() {
349 if (converterMessage != null) {
350 return converterMessage;
351 }
352
353 ValueExpression ve = getValueExpression("converterMessage");
354 if (ve != null) {
355 try {
356 return ((String) ve.getValue(getFacesContext().getELContext()));
357 }
358 catch (ELException e) {
359 throw new FacesException(e);
360 }
361 } else {
362 return (null);
363 }
364
365 }
366
367 /**
368 * <p>Override any {@link ValueExpression} set for the "converterMessage"
369 * with the literal argument provided to this method. Subsequent calls
370 * to {@link #getConverterMessage} will return this value;</p>
371 *
372 * @param message the literal message value to be displayed in the event
373 * conversion fails.
374 */
375
376 public void setConverterMessage(String message) {
377 converterMessage = message;
378 }
379
380 private String validatorMessage;
381
382 /**
383 * <p>If there has been a call to {@link #setValidatorMessage} on this
384 * instance, return the message. Otherwise, call {@link #getValueExpression}
385 * passing the key "validatorMessage", get the result of the expression, and return it.
386 * Any {@link ELException}s thrown during the call to <code>getValue()</code>
387 * must be wrapped in a {@link FacesException} and rethrown.
388 */
389
390 public String getValidatorMessage() {
391 if (validatorMessage != null) {
392 return validatorMessage;
393 }
394
395 ValueExpression ve = getValueExpression("validatorMessage");
396 if (ve != null) {
397 try {
398 return ((String) ve.getValue(getFacesContext().getELContext()));
399 }
400 catch (ELException e) {
401 throw new FacesException(e);
402 }
403 } else {
404 return (null);
405 }
406
407 }
408
409 /**
410 * <p>Override any {@link ValueExpression} set for the "validatorMessage"
411 * with the literal argument provided to this method. Subsequent calls
412 * to {@link #getValidatorMessage} will return this value;</p>
413 *
414 * @param message the literal message value to be displayed in the event
415 * validation fails.
416 */
417
418 public void setValidatorMessage(String message) {
419 validatorMessage = message;
420 }
421
422 private boolean valid = true;
423
424
425 public boolean isValid() {
426
427 return (this.valid);
428
429 }
430
431
432 public void setValid(boolean valid) {
433
434 this.valid = valid;
435
436 }
437
438
439 /**
440 * <p>Set the "required field" state for this component.</p>
441 *
442 * @param required The new "required field" state
443 */
444 public void setRequired(boolean required) {
445
446 this.required = required;
447
448 }
449
450 /**
451 * <p>The immediate flag.</p>
452 */
453 private Boolean immediate;
454
455
456 public boolean isImmediate() {
457
458 if (this.immediate != null) {
459 return (this.immediate);
460 }
461 ValueExpression ve = getValueExpression("immediate");
462 if (ve != null) {
463 try {
464 return (Boolean.TRUE.equals(ve.getValue(getFacesContext().getELContext())));
465 }
466 catch (ELException e) {
467 throw new FacesException(e);
468 }
469
470 } else {
471 return (false);
472 }
473
474 }
475
476
477 public void setImmediate(boolean immediate) {
478
479 this.immediate = immediate;
480
481 }
482
483
484 /**
485 * <p>Return a <code>MethodBinding</code> pointing at a
486 * method that will be called during <em>Process Validations</em>
487 * phase of the request processing lifecycle, to validate the current
488 * value of this component.</p>
489 *
490 * @deprecated {@link #getValidators} should be used instead.
491 */
492 public MethodBinding getValidator() {
493 MethodBinding result = null;
494
495 Validator[] curValidators = getValidators();
496 // go through our lisetners list and find the one and only
497 // MethodBindingValidator instance, if present.
498 if (null != curValidators) {
499 for (int i = 0; i < curValidators.length; i++) {
500 // We are guaranteed to have at most one instance of
501 // MethodBindingValidator in the curValidators list.
502 if (MethodBindingValidator.class ==
503 curValidators[i].getClass()) {
504 result = ((MethodBindingValidator) curValidators[i]).
505 getWrapped();
506 break;
507 }
508 }
509 }
510 return result;
511
512 }
513
514
515 /**
516 * <p>Set a <code>MethodBinding</code> pointing at a
517 * method that will be called during <em>Process Validations</em>
518 * phase of the request processing lifecycle, to validate the current
519 * value of this component.</p>
520 * <p/>
521 * <p>Any method referenced by such an expression must be public, with
522 * a return type of <code>void</code>, and accept parameters of type
523 * {@link FacesContext}, {@link UIComponent}, and <code>Object</code>.</p>
524 *
525 * @param validatorBinding The new <code>MethodBinding</code> instance
526 * @deprecated Use {@link #addValidator} instead, obtaining the
527 * argument {@link Validator} by creating an instance of {@link
528 * javax.faces.validator.MethodExpressionValidator}.
529 */
530 public void setValidator(MethodBinding validatorBinding) {
531 Validator[] curValidators = getValidators();
532 // see if we need to null-out, or replace an existing validator
533 if (null != curValidators) {
534 for (int i = 0; i < curValidators.length; i++) {
535 // if we want to remove the validatorBinding
536 if (null == validatorBinding) {
537 // We are guaranteed to have at most one instance of
538 // MethodBindingValidator in the curValidators
539 // list.
540 if (MethodBindingValidator.class ==
541 curValidators[i].getClass()) {
542 removeValidator(curValidators[i]);
543 return;
544 }
545 }
546 // if we want to replace the validatorBinding
547 else //noinspection ObjectEquality
548 if (validatorBinding == curValidators[i]) {
549 removeValidator(curValidators[i]);
550 break;
551 }
552 }
553 }
554 addValidator(new MethodBindingValidator(validatorBinding));
555
556 }
557
558 public MethodBinding getValueChangeListener() {
559 MethodBinding result = null;
560
561 ValueChangeListener[] curListeners = getValueChangeListeners();
562 // go through our lisetners list and find the one and only
563 // MethodBindingValueChangeListener instance, if present.
564 if (null != curListeners) {
565 for (int i = 0; i < curListeners.length; i++) {
566 // We are guaranteed to have at most one instance of
567 // MethodBindingValueChangeListener in the curListeners list.
568 if (MethodBindingValueChangeListener.class ==
569 curListeners[i].getClass()) {
570 result = ((MethodBindingValueChangeListener) curListeners[i]).
571 getWrapped();
572 break;
573 }
574 }
575 }
576 return result;
577 }
578
579
580 /**
581 * {@inheritDoc}
582 *
583 * @deprecated Use {@link #addValueChangeListener} instead, obtaining the
584 * argument {@link ValueChangeListener} by creating an instance of {@link
585 * javax.faces.event.MethodExpressionValueChangeListener}.
586 */
587 public void setValueChangeListener(MethodBinding valueChangeListener) {
588
589 ValueChangeListener[] curListeners = getValueChangeListeners();
590 // see if we need to null-out, or replace an existing listener
591 if (null != curListeners) {
592 for (int i = 0; i < curListeners.length; i++) {
593 // if we want to remove the valueChangeListener
594 if (null == valueChangeListener) {
595 // We are guaranteed to have at most one instance of
596 // MethodBindingValueChangeListener in the curListeners
597 // list.
598 if (MethodBindingValueChangeListener.class ==
599 curListeners[i].getClass()) {
600 removeFacesListener(curListeners[i]);
601 return;
602 }
603 }
604 // if we want to replace the valueChangeListener
605 else //noinspection ObjectEquality
606 if (valueChangeListener == curListeners[i]) {
607 removeFacesListener(curListeners[i]);
608 break;
609 }
610 }
611 }
612 addValueChangeListener(new MethodBindingValueChangeListener(valueChangeListener));
613 }
614
615 // ----------------------------------------------------- UIComponent Methods
616
617 /**
618 * <p>Specialized decode behavior on top of that provided by the
619 * superclass. In addition to the standard
620 * <code>processDecodes</code> behavior inherited from {@link
621 * UIComponentBase}, calls <code>validate()</code> if the the
622 * <code>immediate</code> property is true; if the component is
623 * invalid afterwards or a <code>RuntimeException</code> is thrown,
624 * calls {@link FacesContext#renderResponse}. </p>
625 *
626 * @throws NullPointerException {@inheritDoc}
627 */
628 public void processDecodes(FacesContext context) {
629
630 if (context == null) {
631 throw new NullPointerException();
632 }
633
634 // Skip processing if our rendered flag is false
635 if (!isRendered()) {
636 return;
637 }
638
639 super.processDecodes(context);
640
641 if (isImmediate()) {
642 executeValidate(context);
643 }
644 }
645
646 /**
647 * <p>In addition to the standard <code>processValidators</code> behavior
648 * inherited from {@link UIComponentBase}, calls <code>validate()</code>
649 * if the <code>immediate</code> property is false (which is the
650 * default); if the component is invalid afterwards, calls
651 * {@link FacesContext#renderResponse}.
652 * If a <code>RuntimeException</code> is thrown during
653 * validation processing, calls {@link FacesContext#renderResponse}
654 * and re-throw the exception.
655 * </p>
656 *
657 * @throws NullPointerException {@inheritDoc}
658 */
659 public void processValidators(FacesContext context) {
660
661 if (context == null) {
662 throw new NullPointerException();
663 }
664
665 // Skip processing if our rendered flag is false
666 if (!isRendered()) {
667 return;
668 }
669
670 super.processValidators(context);
671 if (!isImmediate()) {
672 executeValidate(context);
673 }
674 }
675
676 /**
677 * <p>In addition to the standard <code>processUpdates</code> behavior
678 * inherited from {@link UIComponentBase}, calls
679 * <code>updateModel()</code>.
680 * If the component is invalid afterwards, calls
681 * {@link FacesContext#renderResponse}.
682 * If a <code>RuntimeException</code> is thrown during
683 * update processing, calls {@link FacesContext#renderResponse}
684 * and re-throw the exception.
685 * </p>
686 *
687 * @throws NullPointerException {@inheritDoc}
688 */
689 public void processUpdates(FacesContext context) {
690
691 if (context == null) {
692 throw new NullPointerException();
693 }
694
695 // Skip processing if our rendered flag is false
696 if (!isRendered()) {
697 return;
698 }
699
700 super.processUpdates(context);
701
702 try {
703 updateModel(context);
704 } catch (RuntimeException e) {
705 context.renderResponse();
706 throw e;
707 }
708
709 if (!isValid()) {
710 context.renderResponse();
711 }
712 }
713
714 /**
715 * @throws NullPointerException {@inheritDoc}
716 */
717 public void decode(FacesContext context) {
718
719 if (context == null) {
720 throw new NullPointerException();
721 }
722
723 // Force validity back to "true"
724 setValid(true);
725 super.decode(context);
726 }
727
728 /**
729 * <p>Perform the following algorithm to update the model data
730 * associated with this {@link UIInput}, if any, as appropriate.</p>
731 * <ul>
732 * <li>If the <code>valid</code> property of this component is
733 * <code>false</code>, take no further action.</li>
734 * <li>If the <code>localValueSet</code> property of this component is
735 * <code>false</code>, take no further action.</li>
736 * <li>If no {@link ValueExpression} for <code>value</code> exists,
737 * take no further action.</li>
738 * <li>Call <code>setValue()</code> method of the {@link ValueExpression}
739 * to update the value that the {@link ValueExpression} points at.</li>
740 * <li>If the <code>setValue()</code> method returns successfully:
741 * <ul>
742 * <li>Clear the local value of this {@link UIInput}.</li>
743 * <li>Set the <code>localValueSet</code> property of this
744 * {@link UIInput} to false.</li>
745 * </ul></li>
746 * <li>If the <code>setValue()</code> method call fails:
747 * <ul>
748 * <li>Enqueue an error message by calling <code>addMessage()</code>
749 * on the specified {@link FacesContext} instance.</li>
750 * <li>Set the <code>valid</code> property of this {@link UIInput}
751 * to <code>false</code>.</li>
752 * </ul></li>
753 * </ul>
754 *
755 * @param context {@link FacesContext} for the request we are processing
756 * @throws NullPointerException if <code>context</code>
757 * is <code>null</code>
758 */
759 public void updateModel(FacesContext context) {
760
761 if (context == null) {
762 throw new NullPointerException();
763 }
764
765 if (!isValid() || !isLocalValueSet()) {
766 return;
767 }
768 ValueExpression ve = getValueExpression("value");
769 if (ve != null) {
770 try {
771 ve.setValue(context.getELContext(), getLocalValue());
772 setValue(null);
773 setLocalValueSet(false);
774 } catch (ELException e) {
775 String messageStr = e.getMessage();
776 Throwable result = e.getCause();
777 while (null != result &&
778 result.getClass().isAssignableFrom(ELException.class)) {
779 messageStr = result.getMessage();
780 result = result.getCause();
781 }
782 FacesMessage message;
783 if (null == messageStr) {
784 message =
785 MessageFactory.getMessage(context, UPDATE_MESSAGE_ID,
786 MessageFactory.getLabel(
787 context, this));
788 } else {
789 message = new FacesMessage(FacesMessage.SEVERITY_ERROR,
790 messageStr,
791 messageStr);
792 }
793 LOGGER.log(Level.SEVERE, message.getSummary(), result);
794 context.addMessage(getClientId(context), message);
795 setValid(false);
796 } catch (IllegalArgumentException e) {
797 FacesMessage message =
798 MessageFactory.getMessage(context, UPDATE_MESSAGE_ID,
799 MessageFactory.getLabel(
800 context, this));
801 LOGGER.log(Level.SEVERE, message.getSummary(), e);
802 context.addMessage(getClientId(context), message);
803 setValid(false);
804 } catch (Exception e) {
805 FacesMessage message =
806 MessageFactory.getMessage(context, UPDATE_MESSAGE_ID,
807 MessageFactory.getLabel(
808 context, this));
809 LOGGER.log(Level.SEVERE, message.getSummary(), e);
810 context.addMessage(getClientId(context), message);
811 setValid(false);
812 }
813 }
814 }
815
816 // ------------------------------------------------------ Validation Methods
817
818
819 /**
820 * <p>Perform the following algorithm to validate the local value of
821 * this {@link UIInput}.</p>
822 * <ul>
823 * <li>Retrieve the submitted value with <code>getSubmittedValue()</code>.
824 * If this returns null, exit without further processing. (This
825 * indicates that no value was submitted for this component.)</li>
826 * <p/>
827 * <li> Convert the submitted value into a "local value" of the
828 * appropriate data type by calling {@link #getConvertedValue}.</li>
829 * <p/>
830 * <li>Validate the property by calling {@link #validateValue}.</li>
831 * <p/>
832 * <li>If the <code>valid</code> property of this component is still
833 * <code>true</code>, retrieve the previous value of the component
834 * (with <code>getValue()</code>), store the new local value using
835 * <code>setValue()</code>, and reset the submitted value to
836 * null. If the local value is different from
837 * the previous value of this component, fire a
838 * {@link ValueChangeEvent} to be broadcast to all interested
839 * listeners.</li>
840 * </ul>
841 * <p/>
842 * <p>Application components implementing {@link UIInput} that wish to
843 * perform validation with logic embedded in the component should perform
844 * their own correctness checks, and then call the
845 * <code>super.validate()</code> method to perform the standard
846 * processing described above.</p>
847 *
848 * @param context The {@link FacesContext} for the current request
849 * @throws NullPointerException if <code>context</code>
850 * is null
851 */
852 public void validate(FacesContext context) {
853
854 if (context == null) {
855 throw new NullPointerException();
856 }
857
858 // Submitted value == null means "the component was not submitted
859 // at all"; validation should not continue
860 Object submittedValue = getSubmittedValue();
861 if (submittedValue == null) {
862 return;
863 }
864
865 Object newValue = null;
866
867 try {
868 newValue = getConvertedValue(context, submittedValue);
869 }
870 catch (ConverterException ce) {
871 addConversionErrorMessage(context, ce, submittedValue);
872 setValid(false);
873 }
874
875 validateValue(context, newValue);
876
877 // If our value is valid, store the new value, erase the
878 // "submitted" value, and emit a ValueChangeEvent if appropriate
879 if (isValid()) {
880 Object previous = getValue();
881 setValue(newValue);
882 setSubmittedValue(null);
883 if (compareValues(previous, newValue)) {
884 queueEvent(new ValueChangeEvent(this, previous, newValue));
885 }
886 }
887
888 }
889
890 /**
891 * <p>Convert the submitted value into a "local value" of the
892 * appropriate data type, if necessary. Employ the following
893 * algorithm to do so:</p>
894 * <ul>
895 * <li>If a <code>Renderer</code> is present, call
896 * <code>getConvertedValue()</code> to convert the submitted
897 * value.</li>
898 * <li>If no <code>Renderer</code> is present, and the submitted
899 * value is a String, locate a {@link Converter} as follows:
900 * <ul>
901 * <li>If <code>getConverter()</code> returns a non-null {@link Converter},
902 * use that instance.</li>
903 * <li>Otherwise, if a value binding for <code>value</code> exists,
904 * call <code>getType()</code> on it.
905 * <ul>
906 * <li>If this call returns <code>null</code>, assume the output
907 * type is <code>String</code> and perform no conversion.</li>
908 * <li>Otherwise, call
909 * <code>Application.createConverter(Class)</code>
910 * to locate any registered {@link Converter} capable of
911 * converting data values of the specified type.</li>
912 * </ul>
913 * </li>
914 * </ul>
915 * <li>If a {@link Converter} instance was located, call its
916 * <code>getAsObject()</code> method to perform the conversion.
917 * If conversion fails:
918 * <ul>
919 * <li>Enqueue an appropriate error message by calling the
920 * <code>addMessage()</code> method on the
921 * <code>FacesContext</code>.</li>
922 * <li>Set the <code>valid</code> property
923 * on this component to <code>false</code> </li>
924 * </ul></li>
925 * <li>Otherwise, use the submitted value without any conversion</li>
926 * </ul>
927 * </li>
928 * <p/>
929 * </p>
930 * <p/>
931 * <p>This method can be overridden by subclasses for more specific
932 * behavior.</p>
933 */
934
935
936 protected Object getConvertedValue(FacesContext context,
937 Object newSubmittedValue) throws ConverterException {
938 Renderer renderer = getRenderer(context);
939 Object newValue;
940
941 if (renderer != null) {
942 newValue = renderer.getConvertedValue(context, this,
943 newSubmittedValue);
944 } else if (newSubmittedValue instanceof String) {
945 // If there's no Renderer, and we've got a String,
946 // run it through the Converter (if any)
947 Converter converter = getConverterWithType(context);
948 if (converter != null) {
949 newValue = converter.getAsObject(context, this,
950 (String) newSubmittedValue);
951 } else {
952 newValue = newSubmittedValue;
953 }
954 } else {
955 newValue = newSubmittedValue;
956 }
957 return newValue;
958 }
959
960 /**
961 * <p>Set the "valid" property according to the below algorithm.</p>
962 * <p/>
963 * <ul>
964 * <p/>
965 * <li>If the <code>valid</code> property on this component is still
966 * <code>true</code>, and the <code>required</code> property is also
967 * true, ensure that the local value is not empty (where "empty" is
968 * defined as <code>null</code> or a zero-length String. If the local
969 * value is empty:
970 * <ul>
971 * <li>Enqueue an appropriate error message by calling the
972 * <code>addMessage()</code> method on the <code>FacesContext</code>
973 * instance for the current request. If the {@link #getRequiredMessage}
974 * returns non-<code>null</code>, use the value as the <code>summary</code>
975 * and <code>detail</code> in the {@link FacesMessage} that
976 * is enqueued on the <code>FacesContext</code>, otherwise
977 * use the message for the {@link #REQUIRED_MESSAGE_ID}.
978 * </li>
979 * <li>Set the <code>valid</code> property on this component to
980 * <code>false</code>.</li>
981 * </ul></li>
982 * <li>If the <code>valid</code> property on this component is still
983 * <code>true</code>, and the local value is not empty, call the
984 * <code>validate()</code> method of each {@link Validator}
985 * registered for this {@link UIInput}, followed by the method
986 * pointed at by the <code>validatorBinding</code> property (if any).
987 * If any of these validators or the method throws a
988 * {@link ValidatorException}, catch the exception, add
989 * its message (if any) to the {@link FacesContext}, and set
990 * the <code>valid</code> property of this component to false.</li>
991 * <p/>
992 * </ul>
993 */
994
995 protected void validateValue(FacesContext context, Object newValue) {
996 // If our value is valid, enforce the required property if present
997 if (isValid() && isRequired() && isEmpty(newValue)) {
998 String requiredMessageStr = getRequiredMessage();
999 FacesMessage message;
1000 if (null != requiredMessageStr) {
1001 message = new FacesMessage(FacesMessage.SEVERITY_ERROR,
1002 requiredMessageStr,
1003 requiredMessageStr);
1004 } else {
1005 message =
1006 MessageFactory.getMessage(context, REQUIRED_MESSAGE_ID,
1007 MessageFactory.getLabel(
1008 context, this));
1009 }
1010 context.addMessage(getClientId(context), message);
1011 setValid(false);
1012 }
1013
1014 // If our value is valid and not empty, call all validators
1015 if (isValid() && !isEmpty(newValue)) {
1016 if (this.validators != null) {
1017 for (Validator validator : this.validators) {
1018 try {
1019 validator.validate(context, this, newValue);
1020 }
1021 catch (ValidatorException ve) {
1022 // If the validator throws an exception, we're
1023 // invalid, and we need to add a message
1024 setValid(false);
1025 FacesMessage message;
1026 String validatorMessageString = getValidatorMessage();
1027
1028 if (null != validatorMessageString) {
1029 message =
1030 new FacesMessage(FacesMessage.SEVERITY_ERROR,
1031 validatorMessageString,
1032 validatorMessageString);
1033 message.setSeverity(FacesMessage.SEVERITY_ERROR);
1034 } else {
1035 message = ve.getFacesMessage();
1036 }
1037 if (message != null) {
1038 context.addMessage(getClientId(context), message);
1039 }
1040 }
1041 }
1042 }
1043 }
1044 }
1045
1046
1047 /**
1048 * <p>Return <code>true</code> if the new value is different from the
1049 * previous value.</p>
1050 *
1051 * @param previous old value of this component (if any)
1052 * @param value new value of this component (if any)
1053 */
1054 protected boolean compareValues(Object previous, Object value) {
1055
1056 if (previous == null) {
1057 return (value != null);
1058 } else if (value == null) {
1059 return (true);
1060 } else {
1061 return (!(previous.equals(value)));
1062 }
1063
1064 }
1065
1066
1067 /**
1068 * Executes validation logic.
1069 */
1070 private void executeValidate(FacesContext context) {
1071 try {
1072 validate(context);
1073 } catch (RuntimeException e) {
1074 context.renderResponse();
1075 throw e;
1076 }
1077
1078 if (!isValid()) {
1079 context.renderResponse();
1080 }
1081 }
1082
1083 private static boolean isEmpty(Object value) {
1084
1085 if (value == null) {
1086 return (true);
1087 } else if ((value instanceof String) &&
1088 (((String) value).length() < 1)) {
1089 return (true);
1090 } else if (value.getClass().isArray()) {
1091 if (0 == java.lang.reflect.Array.getLength(value)) {
1092 return (true);
1093 }
1094 } else if (value instanceof List) {
1095 if (((List) value).isEmpty()) {
1096 return (true);
1097 }
1098 }
1099 return (false);
1100 }
1101
1102
1103 /**
1104 * <p>The set of {@link Validator}s associated with this
1105 * <code>UIComponent</code>.</p>
1106 */
1107 List<Validator> validators = null;
1108
1109
1110 /**
1111 * <p>Add a {@link Validator} instance to the set associated with
1112 * this {@link UIInput}.</p>
1113 *
1114 * @param validator The {@link Validator} to add
1115 * @throws NullPointerException if <code>validator</code>
1116 * is null
1117 */
1118 public void addValidator(Validator validator) {
1119
1120 if (validator == null) {
1121 throw new NullPointerException();
1122 }
1123 if (validators == null) {
1124 //noinspection CollectionWithoutInitialCapacity
1125 validators = new ArrayList<Validator>();
1126 }
1127 validators.add(validator);
1128
1129 }
1130
1131
1132 /**
1133 * <p>Return the set of registered {@link Validator}s for this
1134 * {@link UIInput} instance. If there are no registered validators,
1135 * a zero-length array is returned.</p>
1136 */
1137 public Validator[] getValidators() {
1138
1139 if (validators == null) {
1140 return EMPTY_VALIDATOR;
1141 } else {
1142 return (validators.toArray(new Validator[validators.size()]));
1143 }
1144
1145 }
1146
1147
1148 /**
1149 * <p>Remove a {@link Validator} instance from the set associated with
1150 * this {@link UIInput}, if it was previously associated.
1151 * Otherwise, do nothing.</p>
1152 *
1153 * @param validator The {@link Validator} to remove
1154 */
1155 public void removeValidator(Validator validator) {
1156
1157 if (validators != null) {
1158 validators.remove(validator);
1159 }
1160
1161 }
1162
1163 // ------------------------------------------------ Event Processing Methods
1164
1165
1166 /**
1167 * <p>Add a new {@link ValueChangeListener} to the set of listeners
1168 * interested in being notified when {@link ValueChangeEvent}s occur.</p>
1169 *
1170 * @param listener The {@link ValueChangeListener} to be added
1171 * @throws NullPointerException if <code>listener</code>
1172 * is <code>null</code>
1173 */
1174 public void addValueChangeListener(ValueChangeListener listener) {
1175
1176 addFacesListener(listener);
1177
1178 }
1179
1180
1181 /**
1182 * <p>Return the set of registered {@link ValueChangeListener}s for this
1183 * {@link UIInput} instance. If there are no registered listeners,
1184 * a zero-length array is returned.</p>
1185 */
1186 public ValueChangeListener[] getValueChangeListeners() {
1187
1188 return (ValueChangeListener[]) getFacesListeners(ValueChangeListener.class);
1189 }
1190
1191
1192 /**
1193 * <p>Remove an existing {@link ValueChangeListener} (if any) from the
1194 * set of listeners interested in being notified when
1195 * {@link ValueChangeEvent}s occur.</p>
1196 *
1197 * @param listener The {@link ValueChangeListener} to be removed
1198 * @throws NullPointerException if <code>listener</code>
1199 * is <code>null</code>
1200 */
1201 public void removeValueChangeListener(ValueChangeListener listener) {
1202
1203 removeFacesListener(listener);
1204
1205 }
1206
1207 // ----------------------------------------------------- StateHolder Methods
1208
1209
1210 private Object[] values;
1211
1212 public Object saveState(FacesContext context) {
1213
1214 if (values == null) {
1215 values = new Object[9];
1216 }
1217
1218 values[0] = super.saveState(context);
1219 values[1] = localValueSet;
1220 values[2] = required;
1221 values[3] = requiredMessage;
1222 values[4] = converterMessage;
1223 values[5] = validatorMessage;
1224 values[6] = valid;
1225 values[7] = immediate;
1226 values[8] = saveAttachedState(context, validators);
1227 return (values);
1228
1229 }
1230
1231
1232 public void restoreState(FacesContext context, Object state) {
1233
1234 values = (Object[]) state;
1235 super.restoreState(context, values[0]);
1236 localValueSet = (Boolean) values[1];
1237 required = (Boolean) values[2];
1238 requiredMessage = ((String) values[3]);
1239 converterMessage = ((String) values[4]);
1240 validatorMessage = ((String) values[5]);
1241 valid = (Boolean) values[6];
1242 immediate = (Boolean) values[7];
1243 List<Validator> restoredValidators;
1244 Iterator<Validator> iter;
1245
1246 if (null != (restoredValidators = TypedCollections.dynamicallyCastList((List)
1247 restoreAttachedState(context, values[8]), Validator.class))) {
1248 // if there were some validators registered prior to this
1249 // method being invoked, merge them with the list to be
1250 // restored.
1251 if (null != validators) {
1252 iter = restoredValidators.iterator();
1253 while (iter.hasNext()) {
1254 validators.add(iter.next());
1255 }
1256 } else {
1257 validators = restoredValidators;
1258 }
1259 }
1260
1261 }
1262
1263 private Converter getConverterWithType(FacesContext context) {
1264 Converter converter = getConverter();
1265 if (converter != null) {
1266 return converter;
1267 }
1268
1269 ValueExpression valueExpression = getValueExpression("value");
1270 if (valueExpression == null) {
1271 return null;
1272 }
1273
1274 Class converterType;
1275 try {
1276 converterType = valueExpression.getType(context.getELContext());
1277 }
1278 catch (ELException e) {
1279 throw new FacesException(e);
1280 }
1281
1282 // if converterType is null, String, or Object, assume
1283 // no conversion is needed
1284 if (converterType == null ||
1285 converterType == String.class ||
1286 converterType == Object.class) {
1287 return null;
1288 }
1289
1290 // if getType returns a type for which we support a default
1291 // conversion, acquire an appropriate converter instance.
1292 try {
1293 Application application = context.getApplication();
1294 return application.createConverter(converterType);
1295 } catch (Exception e) {
1296 return (null);
1297 }
1298 }
1299
1300 private void addConversionErrorMessage(FacesContext context,
1301 ConverterException ce, Object value) {
1302 FacesMessage message;
1303 String converterMessageString = getConverterMessage();
1304 if (null != converterMessageString) {
1305 message = new FacesMessage(FacesMessage.SEVERITY_ERROR,
1306 converterMessageString,
1307 converterMessageString);
1308 } else {
1309 message = ce.getFacesMessage();
1310 if (message == null) {
1311 message = MessageFactory.getMessage(context,
1312 CONVERSION_MESSAGE_ID);
1313 if (message.getDetail() == null) {
1314 message.setDetail(ce.getMessage());
1315 }
1316 }
1317 }
1318
1319 context.addMessage(getClientId(context), message);
1320 }
1321
1322 }