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