Coverage Report - javax.faces.component.UIInput
 
Classes in this File Line Coverage Branch Coverage Complexity
UIInput
0%
0/323
0%
0/188
0
UIInput$PropertyKeys
0%
0/12
N/A
0
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one
 3  
  * or more contributor license agreements.  See the NOTICE file
 4  
  * distributed with this work for additional information
 5  
  * regarding copyright ownership.  The ASF licenses this file
 6  
  * to you under the Apache License, Version 2.0 (the
 7  
  * "License"); you may not use this file except in compliance
 8  
  * with the License.  You may obtain a copy of the License at
 9  
  *
 10  
  *   http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing,
 13  
  * software distributed under the License is distributed on an
 14  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 15  
  * KIND, either express or implied.  See the License for the
 16  
  * specific language governing permissions and limitations
 17  
  * under the License.
 18  
  */
 19  
 package javax.faces.component;
 20  
 
 21  
 import java.util.ArrayList;
 22  
 import java.util.Arrays;
 23  
 import java.util.Collection;
 24  
 import java.util.HashMap;
 25  
 import java.util.LinkedList;
 26  
 import java.util.List;
 27  
 import java.util.Map;
 28  
 
 29  
 import javax.el.ValueExpression;
 30  
 import javax.faces.FacesException;
 31  
 import javax.faces.application.FacesMessage;
 32  
 import javax.faces.application.ProjectStage;
 33  
 import javax.faces.context.ExternalContext;
 34  
 import javax.faces.context.FacesContext;
 35  
 import javax.faces.convert.Converter;
 36  
 import javax.faces.convert.ConverterException;
 37  
 import javax.faces.el.EvaluationException;
 38  
 import javax.faces.el.MethodBinding;
 39  
 import javax.faces.event.AbortProcessingException;
 40  
 import javax.faces.event.ExceptionQueuedEvent;
 41  
 import javax.faces.event.ExceptionQueuedEventContext;
 42  
 import javax.faces.event.FacesEvent;
 43  
 import javax.faces.event.PhaseId;
 44  
 import javax.faces.event.PostValidateEvent;
 45  
 import javax.faces.event.PreValidateEvent;
 46  
 import javax.faces.event.ValueChangeEvent;
 47  
 import javax.faces.event.ValueChangeListener;
 48  
 import javax.faces.render.Renderer;
 49  
 import javax.faces.validator.Validator;
 50  
 import javax.faces.webapp.FacesServlet;
 51  
 
 52  
 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFComponent;
 53  
 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFListener;
 54  
 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
 55  
 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
 56  
 
 57  
 /**
 58  
  * UICommand is a base abstraction for components that implement ActionSource.
 59  
  * <p>
 60  
  * See the javadoc for this class in the <a href="http://java.sun.com/j2ee/javaserverfaces/1.2/docs/api/index.html">JSF
 61  
  * Specification</a> for further details.
 62  
  * <p>
 63  
  */
 64  
 @JSFComponent(defaultRendererType = "javax.faces.Text")
 65  
 public class UIInput extends UIOutput implements EditableValueHolder
 66  
 {
 67  
     public static final String COMPONENT_TYPE = "javax.faces.Input";
 68  
     public static final String COMPONENT_FAMILY = "javax.faces.Input";
 69  
 
 70  
     public static final String CONVERSION_MESSAGE_ID = "javax.faces.component.UIInput.CONVERSION";
 71  
     public static final String REQUIRED_MESSAGE_ID = "javax.faces.component.UIInput.REQUIRED";
 72  
     public static final String UPDATE_MESSAGE_ID = "javax.faces.component.UIInput.UPDATE";
 73  
 
 74  
     /** -=Leonardo Uribe =- According to http://wiki.java.net/bin/view/Projects/Jsf2MR1ChangeLog 
 75  
       * this constant will be made public on 2.1. For now, since this param is handled in
 76  
       * 2.0, we should do it as well.
 77  
       **/
 78  
     @JSFWebConfigParam(defaultValue="false", expectedValues="true, false", since="2.0")
 79  
     private static final String EMPTY_VALUES_AS_NULL_PARAM_NAME = "javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL";
 80  
     
 81  
     @JSFWebConfigParam(defaultValue="auto", expectedValues="auto, true, false", since="2.0")
 82  
     public static final String VALIDATE_EMPTY_FIELDS_PARAM_NAME = "javax.faces.VALIDATE_EMPTY_FIELDS";
 83  
     
 84  
     /**
 85  
      * Extended debug info is stored under this key in the request
 86  
      * map for every UIInput component when in Development mode.
 87  
      * ATTENTION: this constant is duplicate in org.apache.myfaces.renderkit.ErrorPageWriter
 88  
      */
 89  
     private static final String DEBUG_INFO_KEY = "org.apache.myfaces.debug.DEBUG_INFO";
 90  
 
 91  0
     private static final Validator[] EMPTY_VALIDATOR_ARRAY = new Validator[0];
 92  
     
 93  
     private _DeltaList<Validator> _validatorList;
 94  
 
 95  
     /**
 96  
      * Construct an instance of the UIInput.
 97  
      */
 98  
     public UIInput()
 99  0
     {
 100  0
         setRendererType("javax.faces.Text");
 101  0
     }
 102  
 
 103  
     @Override
 104  
     public String getFamily()
 105  
     {
 106  0
         return COMPONENT_FAMILY;
 107  
     }
 108  
 
 109  
     /**
 110  
      * Store the specified object as the "local value" of this component. The value-binding named "value" (if any) is
 111  
      * ignored; the object is only stored locally on this component. During the "update model" phase, if there is a
 112  
      * value-binding named "value" then this local value will be stored via that value-binding and the "local value"
 113  
      * reset to null.
 114  
      */
 115  
     @Override
 116  
     public void setValue(Object value)
 117  
     {
 118  0
         if (getFacesContext().isProjectStage(ProjectStage.Development))
 119  
         {
 120  
             // extended debug-info when in Development mode
 121  0
             _createFieldDebugInfo(getFacesContext(), "localValue",
 122  
                     getLocalValue(), value, 1);
 123  
         }
 124  0
         setLocalValueSet(true);
 125  0
         super.setValue(value);
 126  0
     }
 127  
     
 128  
     /**
 129  
      * Return the current value of this component.
 130  
      * <p>
 131  
      * If a submitted value has been converted but not yet pushed into the
 132  
      * model, then return that locally-cached value (see isLocalValueSet).
 133  
      * <p>
 134  
      * Otherwise, evaluate an EL expression to fetch a value from the model. 
 135  
      */
 136  
     public Object getValue()
 137  
     {
 138  0
         if (isLocalValueSet()) return super.getLocalValue();
 139  0
         return super.getValue();
 140  
     }
 141  
 
 142  
     /**
 143  
      * Set the "submitted value" of this component from the relevant data in the current servlet request object.
 144  
      * <p>
 145  
      * If this component is not rendered, then do nothing; no output would have been sent to the client so no input is
 146  
      * expected.
 147  
      * <p>
 148  
      * Invoke the inherited functionality, which typically invokes the renderer associated with this component to
 149  
      * extract and set this component's "submitted value".
 150  
      * <p>
 151  
      * If this component is marked "immediate", then immediately apply validation to the submitted value found. On
 152  
      * error, call context method "renderResponse" which will force processing to leap to the "render
 153  
      * response" phase as soon as the "decode" step has completed for all other components.
 154  
      */
 155  
     @Override
 156  
     public void processDecodes(FacesContext context)
 157  
     {
 158  0
         if (context == null)
 159  
         {
 160  0
             throw new NullPointerException("context");
 161  
         }
 162  
         try
 163  
         {
 164  0
             setCachedFacesContext(context);
 165  0
             if (!isRendered())
 166  
             {
 167  
                 return;
 168  
             }
 169  
         }
 170  
         finally
 171  
         {
 172  0
             setCachedFacesContext(null);
 173  0
         }
 174  0
         super.processDecodes(context);
 175  
         try
 176  
         {
 177  0
             setCachedFacesContext(context);
 178  0
             if (isImmediate())
 179  
             {
 180  
                 try
 181  
                 {
 182  
                     //Pre validation event dispatch for component
 183  0
                     context.getApplication().publishEvent(context,  PreValidateEvent.class, UIComponent.class, this);
 184  
     
 185  0
                     validate(context);
 186  
                 }
 187  0
                 catch (RuntimeException e)
 188  
                 {
 189  0
                     context.renderResponse();
 190  0
                     throw e;
 191  
                 }
 192  
                 finally
 193  
                 {
 194  0
                     context.getApplication().publishEvent(context,  PostValidateEvent.class, UIComponent.class, this);
 195  0
                 }
 196  0
                 if (!isValid())
 197  
                 {
 198  0
                     context.renderResponse();
 199  
                 }
 200  
             }
 201  
         }
 202  
         finally
 203  
         {
 204  0
             setCachedFacesContext(null);
 205  0
         }
 206  0
     }
 207  
 
 208  
     @Override
 209  
     public void processValidators(FacesContext context)
 210  
     {
 211  0
         if (context == null)
 212  
         {
 213  0
             throw new NullPointerException("context");
 214  
         }
 215  
         try
 216  
         {
 217  0
             setCachedFacesContext(context);
 218  0
             if (!isRendered())
 219  
             {
 220  
                 return;
 221  
             }
 222  
         }
 223  
         finally
 224  
         {
 225  0
             setCachedFacesContext(null);
 226  0
         }
 227  
 
 228  0
         super.processValidators(context);
 229  
 
 230  
         try
 231  
         {
 232  0
             setCachedFacesContext(context);
 233  0
             if (!isImmediate())
 234  
             {
 235  
                 try
 236  
                 {
 237  
                     //Pre validation event dispatch for component
 238  0
                     context.getApplication().publishEvent(context,  PreValidateEvent.class, UIComponent.class, this);
 239  
     
 240  0
                     validate(context);
 241  
                 }
 242  0
                 catch (RuntimeException e)
 243  
                 {
 244  0
                     context.renderResponse();
 245  0
                     throw e;
 246  
                 }
 247  
                 finally
 248  
                 {
 249  0
                     context.getApplication().publishEvent(context,  PostValidateEvent.class, UIComponent.class, this);
 250  0
                 }
 251  0
                 if (!isValid())
 252  
                 {
 253  0
                     context.validationFailed();
 254  0
                     context.renderResponse();
 255  
                 }
 256  
             }
 257  
         }
 258  
         finally
 259  
         {
 260  0
             setCachedFacesContext(null);
 261  0
         }
 262  0
     }
 263  
 
 264  
     @Override
 265  
     public void processUpdates(FacesContext context)
 266  
     {
 267  0
         if (context == null)
 268  
         {
 269  0
             throw new NullPointerException("context");
 270  
         }
 271  
         try
 272  
         {
 273  0
             setCachedFacesContext(context);
 274  0
             if (!isRendered())
 275  
             {
 276  
                 return;
 277  
             }
 278  
         }
 279  
         finally
 280  
         {
 281  0
             setCachedFacesContext(null);
 282  0
         }
 283  0
         super.processUpdates(context);
 284  
 
 285  
         try
 286  
         {
 287  0
             setCachedFacesContext(context);
 288  
             try
 289  
             {
 290  0
                 updateModel(context);
 291  
             }
 292  0
             catch (RuntimeException e)
 293  
             {
 294  0
                 context.renderResponse();
 295  0
                 throw e;
 296  0
             }
 297  0
             if (!isValid())
 298  
             {
 299  0
                 context.renderResponse();
 300  
             }
 301  
         }
 302  
         finally
 303  
         {
 304  0
             setCachedFacesContext(null);
 305  0
         }
 306  0
     }
 307  
 
 308  
     @Override
 309  
     public void decode(FacesContext context)
 310  
     {
 311  
         // We (re)set to valid, so that component automatically gets (re)validated
 312  0
         setValid(true);
 313  0
         super.decode(context);
 314  0
     }
 315  
 
 316  
     @Override
 317  
     public void broadcast(FacesEvent event) throws AbortProcessingException
 318  
     {
 319  
         // invoke standard listeners attached to this component first
 320  0
         super.broadcast(event);
 321  
 
 322  
         // Check if the event is applicable for ValueChangeListener
 323  0
         if (event instanceof ValueChangeEvent)
 324  
         {
 325  
             // invoke the single listener defined directly on the component
 326  0
             MethodBinding valueChangeListenerBinding = getValueChangeListener();
 327  0
             if (valueChangeListenerBinding != null)
 328  
             {
 329  
                 try
 330  
                 {
 331  0
                     valueChangeListenerBinding.invoke(getFacesContext(), new Object[] { event });
 332  
                 }
 333  0
                 catch (EvaluationException e)
 334  
                 {
 335  0
                     Throwable cause = e.getCause();
 336  0
                     if (cause != null && cause instanceof AbortProcessingException)
 337  
                     {
 338  0
                         throw (AbortProcessingException) cause;
 339  
                     }
 340  
                     else
 341  
                     {
 342  0
                         throw e;
 343  
                     }
 344  0
                 }
 345  
             }
 346  
         }
 347  0
     }
 348  
 
 349  
     public void updateModel(FacesContext context)
 350  
     {
 351  0
         if (!isValid())
 352  
         {
 353  0
             return;
 354  
         }
 355  0
         if (!isLocalValueSet())
 356  
         {
 357  0
             return;
 358  
         }
 359  0
         ValueExpression expression = getValueExpression("value");
 360  0
         if (expression == null)
 361  
         {
 362  0
             return;
 363  
         }
 364  
 
 365  
         try
 366  
         {
 367  0
             expression.setValue(context.getELContext(), getLocalValue());
 368  0
             setValue(null);
 369  0
             setLocalValueSet(false);
 370  
         }
 371  0
         catch (Exception e)
 372  
         {
 373  
             // Enqueue an error message
 374  0
             context.getExternalContext().log(e.getMessage(), e);
 375  
             
 376  
             // Create a FacesMessage with the id UPDATE_MESSAGE_ID
 377  0
             FacesMessage facesMessage = _MessageUtils.getMessage(context,
 378  
                     context.getViewRoot().getLocale(), FacesMessage.SEVERITY_ERROR, UPDATE_MESSAGE_ID,
 379  
                     new Object[] { _MessageUtils.getLabel(context, this) });
 380  
             
 381  
             // create an UpdateModelException and enqueue it since 
 382  
             // we are not allowed to throw it directly here
 383  
             // spec javadoc: The exception must not be re-thrown. This enables tree traversal to 
 384  
             // continue for this lifecycle phase, as in all the other lifecycle phases.
 385  0
             UpdateModelException updateModelException = new UpdateModelException(facesMessage, e);
 386  0
             ExceptionQueuedEventContext exceptionQueuedContext 
 387  
                     = new ExceptionQueuedEventContext (context, updateModelException, this, PhaseId.UPDATE_MODEL_VALUES);
 388  
             
 389  
             // spec javadoc says we should call context.getExceptionHandler().processEvent(exceptionQueuedContext),
 390  
             // which is not just syntactically wrong, but also stupid!!
 391  0
             context.getApplication().publishEvent(context, ExceptionQueuedEvent.class, exceptionQueuedContext);
 392  
             
 393  
             // Set the valid property of this UIInput to false
 394  0
             setValid(false);
 395  0
         }
 396  0
     }
 397  
 
 398  
     protected void validateValue(FacesContext context, Object convertedValue)
 399  
     {
 400  0
         if (!isValid())
 401  0
             return;
 402  
 
 403  
         // If our value is empty, check the required property
 404  0
         boolean isEmpty = isEmpty(convertedValue); 
 405  
 
 406  0
         if (isRequired() && isEmpty)
 407  
         {
 408  0
             if (getRequiredMessage() != null)
 409  
             {
 410  0
                 String requiredMessage = getRequiredMessage();
 411  0
                 context.addMessage(this.getClientId(context), new FacesMessage(FacesMessage.SEVERITY_ERROR,
 412  
                     requiredMessage, requiredMessage));
 413  0
             }
 414  
             else
 415  
             {
 416  0
                 _MessageUtils.addErrorMessage(context, this, REQUIRED_MESSAGE_ID,
 417  
                     new Object[] { _MessageUtils.getLabel(context, this) });
 418  
             }
 419  0
             setValid(false);
 420  0
             return;
 421  
         }
 422  
 
 423  0
         if (!isEmpty || shouldValidateEmptyFields(context))
 424  
         {
 425  0
             _ComponentUtils.callValidators(context, this, convertedValue);
 426  
         }
 427  0
     }
 428  
 
 429  
     private boolean shouldValidateEmptyFields(FacesContext context)
 430  
     {
 431  0
         ExternalContext extCtx = context.getExternalContext();
 432  0
         String validateEmptyFields = (String) extCtx.getInitParameter(VALIDATE_EMPTY_FIELDS_PARAM_NAME);
 433  0
         if (validateEmptyFields == null)
 434  
         {
 435  0
             validateEmptyFields = (String) extCtx.getApplicationMap().get(VALIDATE_EMPTY_FIELDS_PARAM_NAME);
 436  
         }
 437  
 
 438  
         // null means the same as auto.
 439  0
         if (validateEmptyFields == null)
 440  
         {
 441  0
             validateEmptyFields = "auto";
 442  
         }
 443  
         else
 444  
         {
 445  
             // The environment variables are case insensitive.
 446  0
             validateEmptyFields = validateEmptyFields.toLowerCase();
 447  
         }
 448  
 
 449  0
         if (validateEmptyFields.equals("auto") && _ExternalSpecifications.isBeanValidationAvailable())
 450  
         {
 451  0
             return true;
 452  
         }
 453  0
         else if (validateEmptyFields.equals("true"))
 454  
         {
 455  0
             return true;
 456  
         }
 457  0
         return false;
 458  
     }
 459  
 
 460  
     /**
 461  
      * Determine whether the new value is valid, and queue a ValueChangeEvent if necessary.
 462  
      * <p>
 463  
      * The "submitted value" is converted to the necessary type; conversion failure is reported as an error and
 464  
      * validation processing terminates for this component. See documentation for method getConvertedValue for details
 465  
      * on the conversion process.
 466  
      * <p>
 467  
      * Any validators attached to this component are then run, passing the converted value.
 468  
      * <p>
 469  
      * The old value of this component is then fetched (possibly involving the evaluation of a value-binding expression,
 470  
      * ie invoking a method on a user object). The old value is compared to the new validated value, and if they are
 471  
      * different then a ValueChangeEvent is queued for later processing.
 472  
      * <p>
 473  
      * On successful completion of this method:
 474  
      * <ul>
 475  
      * <li>isValid() is true
 476  
      * <li>isLocalValueSet() is true
 477  
      * <li>submittedValue is reset to null
 478  
      * <li>a ValueChangeEvent is queued if the new value != old value
 479  
      * </ul>
 480  
      */
 481  
     public void validate(FacesContext context)
 482  
     {
 483  0
         if (context == null)
 484  0
             throw new NullPointerException("context");
 485  
 
 486  
 
 487  
         try
 488  
         {
 489  0
             Object submittedValue = getSubmittedValue();
 490  0
             if (submittedValue == null)
 491  
             {
 492  0
                 return;
 493  
             }
 494  
 
 495  
             // Begin new JSF 2.0 requirement (INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL)
 496  0
             String contextParam = context.getExternalContext().getInitParameter(EMPTY_VALUES_AS_NULL_PARAM_NAME);
 497  0
             if (contextParam != null && contextParam.toLowerCase().equals("true"))
 498  
             {
 499  0
                 if (submittedValue.toString().length() == 0)
 500  
                 {
 501  0
                     setSubmittedValue(null);
 502  0
                     submittedValue = null;
 503  
                 }
 504  
             }
 505  
             // End new JSF 2.0 requirement (INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL)
 506  
 
 507  
             Object convertedValue;
 508  
             try
 509  
             {
 510  0
                 convertedValue = getConvertedValue(context, submittedValue);
 511  
             }
 512  0
             catch (ConverterException e)
 513  
             {
 514  0
                 String converterMessage = getConverterMessage();
 515  0
                 if (converterMessage != null)
 516  
                 {
 517  0
                     context.addMessage(getClientId(context), new FacesMessage(FacesMessage.SEVERITY_ERROR,
 518  
                         converterMessage, converterMessage));
 519  
                 }
 520  
                 else
 521  
                 {
 522  0
                     FacesMessage facesMessage = e.getFacesMessage();
 523  0
                     if (facesMessage != null)
 524  
                     {
 525  0
                         context.addMessage(getClientId(context), facesMessage);
 526  
                     }
 527  
                     else
 528  
                     {
 529  0
                         _MessageUtils.addErrorMessage(context, this, CONVERSION_MESSAGE_ID,
 530  
                             new Object[] { _MessageUtils.getLabel(context, this) });
 531  
                     }
 532  
                 }
 533  0
                 setValid(false);
 534  0
                 return;
 535  0
             }
 536  
 
 537  0
             validateValue(context, convertedValue);
 538  
 
 539  0
             if (!isValid())
 540  0
                 return;
 541  
 
 542  0
             Object previousValue = getValue();
 543  0
             setValue(convertedValue);
 544  0
             setSubmittedValue(null);
 545  0
             if (compareValues(previousValue, convertedValue))
 546  
             {
 547  0
                 queueEvent(new ValueChangeEvent(this, previousValue, convertedValue));
 548  
             }
 549  
         }
 550  0
         catch (Exception ex)
 551  
         {
 552  0
             throw new FacesException("Exception while validating component with path : "
 553  
                     + _ComponentUtils.getPathToComponent(this), ex);
 554  0
         }
 555  0
     }
 556  
 
 557  
     /**
 558  
      * Convert the provided object to the desired value.
 559  
      * <p>
 560  
      * If there is a renderer for this component, then call the renderer's getConvertedValue method. While this can of
 561  
      * course be implemented in any way the renderer desires, it typically performs exactly the same processing that
 562  
      * this method would have done anyway (ie that described below for the no-renderer case).
 563  
      * <p>
 564  
      * Otherwise:
 565  
      * <ul>
 566  
      * <li>If the submittedValue is not a String then just return the submittedValue unconverted.
 567  
      * <li>If there is no "value" value-binding then just return the submittedValue unconverted.
 568  
      * <li>Use introspection to determine the type of the target property specified by the value-binding, and then use
 569  
      * Application.createConverter to find a converter that can map from String to the required type. Apply the
 570  
      * converter to the submittedValue and return the result.
 571  
      * </ul>
 572  
      */
 573  
     protected Object getConvertedValue(FacesContext context, Object submittedValue) throws ConverterException
 574  
     {
 575  0
         Renderer renderer = getRenderer(context);
 576  0
         if (renderer != null)
 577  
         {
 578  0
             return renderer.getConvertedValue(context, this, submittedValue);
 579  
         }
 580  0
         else if (submittedValue instanceof String)
 581  
         {
 582  0
             Converter converter = _SharedRendererUtils.findUIOutputConverter(context, this);
 583  0
             if (converter != null)
 584  
             {
 585  0
                 return converter.getAsObject(context, this, (String) submittedValue);
 586  
             }
 587  
         }
 588  0
         return submittedValue;
 589  
     }
 590  
 
 591  
     protected boolean compareValues(Object previous, Object value)
 592  
     {
 593  0
         return previous == null ? (value != null) : (!previous.equals(value));
 594  
     }
 595  
 
 596  
     /**
 597  
      * @since 1.2
 598  
      */
 599  
     public void resetValue()
 600  
     {
 601  0
         setSubmittedValue(null);
 602  0
         setValue(null);
 603  0
         setLocalValueSet(false);
 604  0
         setValid(true);
 605  0
     }
 606  
     
 607  
     /**
 608  
      * A boolean value that identifies the phase during which action events should fire.
 609  
      * <p>
 610  
      * During normal event processing, action methods and action listener methods are fired during the
 611  
      * "invoke application" phase of request processing. If this attribute is set to "true", these methods are fired
 612  
      * instead at the end of the "apply request values" phase.
 613  
      * </p>
 614  
      */
 615  
     @JSFProperty
 616  
     public boolean isImmediate()
 617  
     {
 618  0
         return (Boolean) getStateHelper().eval(PropertyKeys.immediate, Boolean.FALSE);
 619  
     }
 620  
 
 621  
     public void setImmediate(boolean immediate)
 622  
     {
 623  0
         getStateHelper().put(PropertyKeys.immediate, immediate );
 624  0
     }
 625  
 
 626  
     /**
 627  
      * A boolean value that indicates whether an input value is required.
 628  
      * <p>
 629  
      * If this value is true and no input value is provided by a postback operation, then the "requiredMessage" text is
 630  
      * registered as a FacesMessage for the request, and validation fails.
 631  
      * </p>
 632  
      * <p>
 633  
      * Default value: false.
 634  
      * </p>
 635  
      */
 636  
     @JSFProperty(defaultValue = "false")
 637  
     public boolean isRequired()
 638  
     {
 639  0
         return (Boolean) getStateHelper().eval(PropertyKeys.required, Boolean.FALSE);
 640  
     }
 641  
 
 642  
     public void setRequired(boolean required)
 643  
     {
 644  0
         getStateHelper().put(PropertyKeys.required, required ); 
 645  0
     }
 646  
 
 647  
     /**
 648  
      * Text to be displayed to the user as an error message when conversion of a submitted value to the target type
 649  
      * fails.
 650  
      * <p>
 651  
      * </p>
 652  
      */
 653  
     @JSFProperty
 654  
     public String getConverterMessage()
 655  
     {
 656  0
         return (String) getStateHelper().eval(PropertyKeys.converterMessage);
 657  
     }
 658  
 
 659  
     public void setConverterMessage(String converterMessage)
 660  
     {
 661  0
         getStateHelper().put(PropertyKeys.converterMessage, converterMessage );
 662  0
     }
 663  
 
 664  
     /**
 665  
      * Text to be displayed to the user as an error message when this component is marked as "required" but no input
 666  
      * data is present during a postback (ie the user left the required field blank).
 667  
      */
 668  
     @JSFProperty
 669  
     public String getRequiredMessage()
 670  
     {
 671  0
         return (String) getStateHelper().eval(PropertyKeys.requiredMessage);
 672  
     }
 673  
 
 674  
     public void setRequiredMessage(String requiredMessage)
 675  
     {
 676  0
         getStateHelper().put(PropertyKeys.requiredMessage, requiredMessage );
 677  0
     }
 678  
 
 679  
     /**
 680  
      * A method-binding EL expression which is invoked during the validation phase for this component.
 681  
      * <p>
 682  
      * The invoked method is expected to check the submitted value for this component, and if not acceptable then report
 683  
      * a validation error for the component.
 684  
      * </p>
 685  
      * <p>
 686  
      * The method is expected to have the prototype
 687  
      * </p>
 688  
      * <code>public void aMethod(FacesContext, UIComponent,Object)</code>
 689  
      * 
 690  
      * @deprecated
 691  
      */
 692  
     @SuppressWarnings("dep-ann")
 693  
     @JSFProperty(stateHolder=true, returnSignature = "void", methodSignature = "javax.faces.context.FacesContext,javax.faces.component.UIComponent,java.lang.Object")
 694  
     public MethodBinding getValidator()
 695  
     {
 696  0
         return (MethodBinding) getStateHelper().eval(PropertyKeys.validator);
 697  
     }
 698  
 
 699  
     /** See getValidator.
 700  
      *  
 701  
      * @deprecated 
 702  
      */
 703  
     public void setValidator(MethodBinding validator)
 704  
     {
 705  0
         getStateHelper().put(PropertyKeys.validator, validator);
 706  0
     }
 707  
 
 708  
     /** See getValidator. */
 709  
     public void addValidator(Validator validator)
 710  
     {
 711  0
         if (validator == null)
 712  
         {
 713  0
             throw new NullPointerException("validator");
 714  
         }
 715  
         
 716  0
         if (_validatorList == null)
 717  
         {
 718  0
             _validatorList = new _DeltaList<Validator>(new ArrayList<Validator>());
 719  
         }
 720  
 
 721  0
         _validatorList.add(validator);
 722  
         
 723  
         // The argument validator must be inspected for the presence of the ResourceDependency annotation.
 724  
         //_handleAnnotations(FacesContext.getCurrentInstance(), validator);
 725  0
     }
 726  
 
 727  
     /** See getValidator. */
 728  
     public void removeValidator(Validator validator)
 729  
     {
 730  0
         if (validator == null || _validatorList == null)
 731  0
             return;
 732  
 
 733  0
         _validatorList.remove(validator);
 734  0
     }
 735  
 
 736  
     /** See getValidator. */
 737  
     public Validator[] getValidators()
 738  
     {
 739  0
         return _validatorList == null ? EMPTY_VALIDATOR_ARRAY
 740  
                 : _validatorList.toArray(new Validator[_validatorList.size()]);
 741  
     }
 742  
 
 743  
     /**
 744  
      * Text which will be shown if validation fails.
 745  
      */
 746  
     @JSFProperty
 747  
     public String getValidatorMessage()
 748  
     {
 749  0
         return (String) getStateHelper().eval(PropertyKeys.validatorMessage);
 750  
     }
 751  
 
 752  
     public void setValidatorMessage(String validatorMessage)
 753  
     {
 754  0
         getStateHelper().put(PropertyKeys.validatorMessage, validatorMessage );
 755  0
     }
 756  
 
 757  
     /**
 758  
      * A method which is invoked during postback processing for the current view if the submitted value for this
 759  
      * component is not equal to the value which the "value" expression for this component returns.
 760  
      * <p>
 761  
      * The phase in which this method is invoked can be controlled via the immediate attribute.
 762  
      * </p>
 763  
      * 
 764  
      * @deprecated
 765  
      */
 766  
     @JSFProperty(stateHolder=true, returnSignature = "void", methodSignature = "javax.faces.event.ValueChangeEvent", clientEvent="valueChange")
 767  
     public MethodBinding getValueChangeListener()
 768  
     {
 769  0
         return (MethodBinding) getStateHelper().eval(PropertyKeys.valueChangeListener);
 770  
     }
 771  
 
 772  
     /**
 773  
      * See getValueChangeListener.
 774  
      * 
 775  
      * @deprecated
 776  
      */
 777  
     public void setValueChangeListener(MethodBinding valueChangeListener)
 778  
     {
 779  0
         getStateHelper().put(PropertyKeys.valueChangeListener, valueChangeListener);
 780  0
     }
 781  
 
 782  
     /**
 783  
      * Specifies whether the component's value is currently valid, ie whether the validators attached to this component
 784  
      * have allowed it.
 785  
      */
 786  
     @JSFProperty(defaultValue = "true", tagExcluded = true)
 787  
     public boolean isValid()
 788  
     {
 789  0
         Object value = getStateHelper().get(PropertyKeys.valid);
 790  0
         if (value != null)
 791  
         {
 792  0
             return (Boolean) value;        
 793  
         }
 794  0
         return true; 
 795  
     }
 796  
 
 797  
     public void setValid(boolean valid)
 798  
     {
 799  
         // default value for valid is true, so if the intention is to save the default
 800  
         // value when nothing else was set before, don't do it. This is done in order to
 801  
         // reduce the size of the saved state of the state helper. Default values won't be
 802  
         // included in the saved state. 
 803  0
         if (getStateHelper().get(PropertyKeys.valid) != null || !valid)
 804  
         {
 805  0
             getStateHelper().put(PropertyKeys.valid, valid );
 806  
         }
 807  0
     }
 808  
 
 809  
     /**
 810  
      * Specifies whether a local value is currently set.
 811  
      * <p>
 812  
      * If false, values are being retrieved from any attached ValueBinding.
 813  
      */
 814  
     @JSFProperty(defaultValue = "false", tagExcluded = true)
 815  
     public boolean isLocalValueSet()
 816  
     {
 817  0
         Object value = getStateHelper().get(PropertyKeys.localValueSet);
 818  0
         if (value != null)
 819  
         {
 820  0
             return (Boolean) value;        
 821  
         }
 822  0
         return false;
 823  
     }
 824  
 
 825  
     public void setLocalValueSet(boolean localValueSet)
 826  
     {
 827  
         // default value for localValueSet is false, so if the intention is to save the default
 828  
         // value when nothing else was set before, don't do it. This is done in order to
 829  
         // reduce the size of the saved state of the state helper. Default values won't be
 830  
         // included in the saved state.
 831  0
         if (getStateHelper().get(PropertyKeys.localValueSet) != null || localValueSet)
 832  
         {
 833  0
             getStateHelper().put(PropertyKeys.localValueSet, localValueSet );
 834  
         }
 835  0
     }
 836  
 
 837  
     /**
 838  
      * Gets the current submitted value. This value, if non-null, is set by the Renderer to store a possibly invalid
 839  
      * value for later conversion or redisplay, and has not yet been converted into the proper type for this component
 840  
      * instance. This method should only be used by the decode() and validate() method of this component, or its
 841  
      * corresponding Renderer; however, user code may manually set it to null to erase any submitted value.
 842  
      */
 843  
     @JSFProperty(tagExcluded = true)
 844  
     public Object getSubmittedValue()
 845  
     {
 846  0
         return  getStateHelper().get(PropertyKeys.submittedValue);
 847  
     }
 848  
 
 849  
     public void setSubmittedValue(Object submittedValue)
 850  
     {
 851  0
         if (getFacesContext().isProjectStage(ProjectStage.Development))
 852  
         {
 853  
             // extended debug-info when in Development mode
 854  0
             _createFieldDebugInfo(getFacesContext(), "submittedValue",
 855  
                     getSubmittedValue(), submittedValue, 1);
 856  
         }
 857  0
         getStateHelper().put(PropertyKeys.submittedValue, submittedValue );
 858  0
     }
 859  
 
 860  
     public void addValueChangeListener(ValueChangeListener listener)
 861  
     {
 862  0
         addFacesListener(listener);
 863  0
     }
 864  
 
 865  
     public void removeValueChangeListener(ValueChangeListener listener)
 866  
     {
 867  0
         removeFacesListener(listener);
 868  0
     }
 869  
 
 870  
     /**
 871  
      * The valueChange event is delivered when the value attribute
 872  
      * is changed.
 873  
      */
 874  
     @JSFListener(event="javax.faces.event.ValueChangeEvent")
 875  
     public ValueChangeListener[] getValueChangeListeners()
 876  
     {
 877  0
         return (ValueChangeListener[]) getFacesListeners(ValueChangeListener.class);
 878  
     }
 879  
 
 880  0
     enum PropertyKeys
 881  
     {
 882  0
          immediate
 883  0
         , required
 884  0
         , converterMessage
 885  0
         , requiredMessage
 886  0
         , validator
 887  0
         , validatorListSet
 888  0
         , validatorMessage
 889  0
         , valueChangeListener
 890  0
         , valid
 891  0
         , localValueSet
 892  0
         , submittedValue
 893  
     }
 894  
     
 895  
     public void markInitialState()
 896  
     {
 897  0
         super.markInitialState();
 898  0
         if (_validatorList != null && 
 899  
             _validatorList instanceof PartialStateHolder)
 900  
         {
 901  0
             ((PartialStateHolder)_validatorList).markInitialState();
 902  
         }
 903  0
     }
 904  
     
 905  
     public void clearInitialState()
 906  
     {
 907  0
         if (initialStateMarked())
 908  
         {
 909  0
             super.clearInitialState();
 910  0
             if (_validatorList != null && 
 911  
                 _validatorList instanceof PartialStateHolder)
 912  
             {
 913  0
                 ((PartialStateHolder)_validatorList).clearInitialState();
 914  
             }
 915  
         }
 916  0
     }    
 917  
 
 918  
     @Override
 919  
     public Object saveState(FacesContext facesContext)
 920  
     {
 921  0
         if (initialStateMarked())
 922  
         {
 923  0
             Object parentSaved = super.saveState(facesContext);
 924  0
             Object validatorListSaved = saveValidatorList(facesContext);
 925  0
             if (parentSaved == null && validatorListSaved == null)
 926  
             {
 927  
                 //No values
 928  0
                 return null;
 929  
             }
 930  
             
 931  0
             Object[] values = new Object[2];
 932  0
             values[0] = parentSaved;
 933  0
             values[1] = validatorListSaved;
 934  0
             return values;
 935  
         }
 936  
         else
 937  
         {
 938  0
             Object[] values = new Object[2];
 939  0
             values[0] = super.saveState(facesContext);
 940  0
             values[1] = saveValidatorList(facesContext);
 941  0
             return values;
 942  
         }
 943  
     }
 944  
 
 945  
     @SuppressWarnings("unchecked")
 946  
     @Override
 947  
     public void restoreState(FacesContext facesContext, Object state)
 948  
     {
 949  0
         if (state == null)
 950  
         {
 951  0
             return;
 952  
         }
 953  
         
 954  0
         Object[] values = (Object[])state;
 955  0
         super.restoreState(facesContext,values[0]);
 956  0
         if (values[1] instanceof _AttachedDeltaWrapper)
 957  
         {
 958  
             //Delta
 959  0
             if (_validatorList != null)
 960  
             {
 961  0
                 ((StateHolder)_validatorList).restoreState(facesContext,
 962  
                         ((_AttachedDeltaWrapper) values[1]).getWrappedStateObject());
 963  
             }
 964  
         }
 965  0
         else if (values[1] != null || !initialStateMarked())
 966  
         {
 967  
             //Full
 968  0
             _validatorList = (_DeltaList<Validator>)
 969  
                 restoreAttachedState(facesContext,values[1]);
 970  
         }
 971  0
     }
 972  
     
 973  
     private Object saveValidatorList(FacesContext facesContext)
 974  
     {
 975  0
         PartialStateHolder holder = (PartialStateHolder) _validatorList;
 976  0
         if (initialStateMarked() && _validatorList != null && holder.initialStateMarked())
 977  
         {                
 978  0
             Object attachedState = holder.saveState(facesContext);
 979  0
             if (attachedState != null)
 980  
             {
 981  0
                 return new _AttachedDeltaWrapper(_validatorList.getClass(),
 982  
                         attachedState);
 983  
             }
 984  
             //_validatorList instances once is created never changes, we can return null
 985  0
             return null;
 986  
         }
 987  
         else
 988  
         {
 989  0
             return saveAttachedState(facesContext,_validatorList);
 990  
         }            
 991  
     }
 992  
     
 993  
     /**
 994  
      * Returns the debug-info Map for this component.
 995  
      * @return
 996  
      */
 997  
     @SuppressWarnings("unchecked")
 998  
     private Map<String, List<Object[]>> _getDebugInfoMap()
 999  
     {
 1000  0
         final Map<String, Object> requestMap = getFacesContext()
 1001  
                 .getExternalContext().getRequestMap();
 1002  0
         Map<String, List<Object[]>> debugInfo = (Map<String, List<Object[]>>) 
 1003  
                 requestMap.get(DEBUG_INFO_KEY + getClientId());
 1004  0
         if (debugInfo == null)
 1005  
         {
 1006  
             // no debug info available yet, create one and put it on the attributes map
 1007  0
             debugInfo = new HashMap<String, List<Object[]>>();
 1008  0
             requestMap.put(DEBUG_INFO_KEY + getClientId(), debugInfo);
 1009  
         }
 1010  0
         return debugInfo;
 1011  
     }
 1012  
     
 1013  
     /**
 1014  
      * Returns the field's debug-infos from the component's debug-info Map.
 1015  
      * @param field
 1016  
      * @return
 1017  
      */
 1018  
     private List<Object[]> _getFieldDebugInfos(final String field)
 1019  
     {
 1020  0
         Map<String, List<Object[]>> debugInfo = _getDebugInfoMap();
 1021  0
         List<Object[]> fieldDebugInfo = debugInfo.get(field);
 1022  0
         if (fieldDebugInfo == null)
 1023  
         {
 1024  
             // no field debug-infos yet, create them and store it in the Map
 1025  0
             fieldDebugInfo = new ArrayList<Object[]>();
 1026  0
             debugInfo.put(field, fieldDebugInfo);
 1027  
         }
 1028  0
         return fieldDebugInfo;
 1029  
     }
 1030  
     
 1031  
     /**
 1032  
      * Creates the field debug-info for the given field, which changed
 1033  
      * from oldValue to newValue.
 1034  
      * 
 1035  
      * @param facesContext
 1036  
      * @param field
 1037  
      * @param oldValue
 1038  
      * @param newValue
 1039  
      * @param skipStackTaceElements How many StackTraceElements should be skipped
 1040  
      *                              when the calling function will be determined.
 1041  
      */
 1042  
     private void _createFieldDebugInfo(FacesContext facesContext,
 1043  
             final String field, Object oldValue, 
 1044  
             Object newValue, final int skipStackTaceElements)
 1045  
     {
 1046  0
         if (oldValue == null && newValue == null)
 1047  
         {
 1048  
             // both values are null, not interesting and can
 1049  
             // happen a lot in UIData with saving and restoring state
 1050  0
             return;
 1051  
         }
 1052  
         
 1053  
         // convert Array values into a more readable format
 1054  0
         if (oldValue != null && oldValue.getClass().isArray())
 1055  
         {
 1056  0
             oldValue = Arrays.deepToString((Object[]) oldValue);
 1057  
         }
 1058  0
         if (newValue != null && newValue.getClass().isArray())
 1059  
         {
 1060  0
             newValue = Arrays.deepToString((Object[]) newValue);
 1061  
         }
 1062  
         
 1063  
         // use Throwable to get the current call stack
 1064  0
         Throwable throwableHelper = new Throwable();
 1065  0
         StackTraceElement[] stackTraceElements = throwableHelper.getStackTrace();
 1066  0
         List<StackTraceElement> debugStackTraceElements = new LinkedList<StackTraceElement>();
 1067  
         
 1068  
         // + 1 because this method should also be skipped
 1069  0
         for (int i = skipStackTaceElements + 1; i < stackTraceElements.length; i++)
 1070  
         {
 1071  0
             debugStackTraceElements.add(stackTraceElements[i]);
 1072  
             
 1073  0
             if (FacesServlet.class.getCanonicalName()
 1074  
                     .equals(stackTraceElements[i].getClassName()))
 1075  
             {
 1076  
                 // stop after the FacesServlet
 1077  0
                 break;
 1078  
             }
 1079  
         }
 1080  
         
 1081  
         // create the debug-info array
 1082  
         // structure:
 1083  
         //     - 0: phase
 1084  
         //     - 1: old value
 1085  
         //     - 2: new value
 1086  
         //     - 3: StackTraceElement List
 1087  
         // NOTE that we cannot create a class here to encapsulate this data,
 1088  
         // because this is not on the spec and the class would not be available in impl.
 1089  0
         Object[] debugInfo = new Object[4];
 1090  0
         debugInfo[0] = facesContext.getCurrentPhaseId();
 1091  0
         debugInfo[1] = oldValue;
 1092  0
         debugInfo[2] = newValue;
 1093  0
         debugInfo[3] = debugStackTraceElements;
 1094  
         
 1095  
         // add the debug info
 1096  0
         _getFieldDebugInfos(field).add(debugInfo);
 1097  0
     }
 1098  
     
 1099  
     /**
 1100  
      * Check if a value is empty or not. Since we don't know the class of
 1101  
      * value we have to check and deal with it properly.
 1102  
      * 
 1103  
      * @since 2.0
 1104  
      * @param value
 1105  
      * @return
 1106  
      */
 1107  
     public static boolean isEmpty(Object value)
 1108  
     {
 1109  0
         if (value == null)
 1110  
         {
 1111  0
             return true;
 1112  
         }
 1113  0
         else if (value instanceof String)
 1114  
         {
 1115  0
             if ( ((String)value).trim().length() <= 0 )
 1116  
             {
 1117  0
                 return true;
 1118  
             }
 1119  
         }
 1120  0
         else if (value instanceof Collection)
 1121  
         {
 1122  0
             if ( ((Collection)value).isEmpty())
 1123  
             {
 1124  0
                 return true;
 1125  
             }
 1126  
         }
 1127  0
         else if (value.getClass().isArray())
 1128  
         {
 1129  0
             if (java.lang.reflect.Array.getLength(value) <= 0)
 1130  
             {
 1131  0
                 return true;
 1132  
             }
 1133  
         }
 1134  0
         else if (value instanceof Map)
 1135  
         {
 1136  0
             if ( ((Map)value).isEmpty())
 1137  
             {
 1138  0
                 return true;
 1139  
             }
 1140  
         }
 1141  0
         return false;
 1142  
     }
 1143  
 
 1144  
 }