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