Coverage Report - org.apache.myfaces.renderkit.html.HtmlAjaxBehaviorRenderer
 
Classes in this File Line Coverage Branch Coverage Complexity
HtmlAjaxBehaviorRenderer
0%
0/196
0%
0/94
5.909
 
 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 org.apache.myfaces.renderkit.html;
 20  
 
 21  
 import java.util.ArrayList;
 22  
 import java.util.Collection;
 23  
 import java.util.Collections;
 24  
 import java.util.List;
 25  
 import java.util.RandomAccess;
 26  
 
 27  
 import javax.faces.FacesException;
 28  
 import javax.faces.component.ActionSource;
 29  
 import javax.faces.component.EditableValueHolder;
 30  
 import javax.faces.component.UIComponent;
 31  
 import javax.faces.component.behavior.AjaxBehavior;
 32  
 import javax.faces.component.behavior.ClientBehavior;
 33  
 import javax.faces.component.behavior.ClientBehaviorContext;
 34  
 import javax.faces.context.FacesContext;
 35  
 import javax.faces.event.AjaxBehaviorEvent;
 36  
 import javax.faces.event.PhaseId;
 37  
 import javax.faces.render.ClientBehaviorRenderer;
 38  
 import org.apache.myfaces.shared.renderkit.html.util.SharedStringBuilder;
 39  
 
 40  
 /**
 41  
  * @author Werner Punz  (latest modification by $Author$)
 42  
  * @version $Revision$ $Date$
 43  
  */
 44  0
 public class HtmlAjaxBehaviorRenderer extends ClientBehaviorRenderer
 45  
 {
 46  
 
 47  
     private static final String QUOTE = "'";
 48  
     private static final String BLANK = " ";
 49  
 
 50  
     private static final String AJAX_KEY_ONERROR = "onerror";
 51  
     private static final String AJAX_KEY_ONEVENT = "onevent";
 52  
     private static final String AJAX_KEY_EXECUTE = "execute";
 53  
     private static final String AJAX_KEY_RENDER = "render";
 54  
     private static final String AJAX_KEY_DELAY = "delay";
 55  
     private static final String AJAX_KEY_RESETVALUES = "resetValues";
 56  
 
 57  
     private static final String AJAX_VAL_THIS = "this";
 58  
     private static final String AJAX_VAL_EVENT = "event";
 59  
     private static final String JS_AJAX_REQUEST = "jsf.ajax.request";
 60  
 
 61  
     private static final String COLON = ":";
 62  
     private static final String EMPTY = "";
 63  
     private static final String COMMA = ",";
 64  
 
 65  
     private static final String ERR_NO_AJAX_BEHAVIOR = "The behavior must be an instance of AjaxBehavior";
 66  
     private static final String L_PAREN = "(";
 67  
     private static final String R_PAREN = ")";
 68  
 
 69  
     /*if this marker is present in the request we have to dispatch a behavior event*/
 70  
     /*if an attached behavior triggers an ajax request this request param must be added*/
 71  
     private static final String BEHAVIOR_EVENT = "javax.faces.behavior.event";
 72  
     private static final String IDENTIFYER_MARKER = "@";
 73  
     
 74  
     private static final String AJAX_SB = "oam.renderkit.AJAX_SB";
 75  
     private static final String AJAX_PARAM_SB = "oam.renderkit.AJAX_PARAM_SB";
 76  
 
 77  
     public void decode(FacesContext context, UIComponent component,
 78  
                        ClientBehavior behavior)
 79  
     {
 80  0
         assertBehavior(behavior);
 81  0
         AjaxBehavior ajaxBehavior = (AjaxBehavior) behavior;
 82  0
         if (ajaxBehavior.isDisabled() || !component.isRendered())
 83  
         {
 84  0
             return;
 85  
         }
 86  
 
 87  0
         dispatchBehaviorEvent(component, ajaxBehavior);
 88  0
     }
 89  
 
 90  
 
 91  
     public String getScript(ClientBehaviorContext behaviorContext,
 92  
                             ClientBehavior behavior)
 93  
     {
 94  0
         assertBehavior(behavior);
 95  0
         AjaxBehavior ajaxBehavior = (AjaxBehavior) behavior;
 96  
 
 97  0
         if (ajaxBehavior.isDisabled())
 98  
         {
 99  0
             return null;
 100  
         }
 101  
 
 102  0
         return makeAjax(behaviorContext, ajaxBehavior).toString();
 103  
     }
 104  
 
 105  
 
 106  
     private final void dispatchBehaviorEvent(UIComponent component, AjaxBehavior ajaxBehavior)
 107  
     {
 108  0
         AjaxBehaviorEvent event = new AjaxBehaviorEvent(component, ajaxBehavior);
 109  
         
 110  0
         boolean isImmediate = false;
 111  0
         if (ajaxBehavior.isImmediateSet())
 112  
         {
 113  0
             isImmediate = ajaxBehavior.isImmediate();
 114  
         }            
 115  
         else
 116  
         {
 117  0
             isImmediate = isComponentImmediate(component);
 118  
         }
 119  0
         PhaseId phaseId = isImmediate ?
 120  
                 PhaseId.APPLY_REQUEST_VALUES :
 121  
                 PhaseId.INVOKE_APPLICATION;
 122  
 
 123  0
         event.setPhaseId(phaseId);
 124  
 
 125  0
         component.queueEvent(event);
 126  0
     }
 127  
 
 128  
 
 129  
     private final boolean isComponentImmediate(UIComponent component)
 130  
     {
 131  0
         boolean isImmediate = false;
 132  0
         if (component instanceof EditableValueHolder)
 133  
         {
 134  0
             isImmediate = ((EditableValueHolder)component).isImmediate();
 135  
         }
 136  0
         else if (component instanceof ActionSource)
 137  
         {
 138  0
             isImmediate = ((ActionSource)component).isImmediate();
 139  
         }
 140  0
         return isImmediate;
 141  
     }
 142  
 
 143  
 
 144  
     /**
 145  
      * builds the generic ajax call depending upon
 146  
      * the ajax behavior parameters
 147  
      *
 148  
      * @param context  the Client behavior context
 149  
      * @param behavior the behavior
 150  
      * @return a fully working javascript with calls into jsf.js
 151  
      */
 152  
     private final StringBuilder makeAjax(ClientBehaviorContext context, AjaxBehavior behavior)
 153  
     {
 154  0
         StringBuilder retVal = SharedStringBuilder.get(context.getFacesContext(), AJAX_SB, 60);
 155  0
         StringBuilder paramBuffer = SharedStringBuilder.get(context.getFacesContext(), AJAX_PARAM_SB, 20);
 156  
 
 157  0
         String executes = mapToString(context, paramBuffer, AJAX_KEY_EXECUTE, behavior.getExecute());
 158  0
         String render = mapToString(context, paramBuffer, AJAX_KEY_RENDER, behavior.getRender());
 159  
 
 160  0
         String onError = behavior.getOnerror();
 161  0
         if (onError != null && !onError.trim().equals(EMPTY))
 162  
         {
 163  
             //onError = AJAX_KEY_ONERROR + COLON + onError;
 164  0
             paramBuffer.setLength(0);
 165  0
             paramBuffer.append(AJAX_KEY_ONERROR);
 166  0
             paramBuffer.append(COLON);
 167  0
             paramBuffer.append(onError);
 168  0
             onError = paramBuffer.toString();
 169  
         }
 170  
         else
 171  
         {
 172  0
             onError = null;
 173  
         }
 174  0
         String onEvent = behavior.getOnevent();
 175  0
         if (onEvent != null && !onEvent.trim().equals(EMPTY))
 176  
         {
 177  0
             paramBuffer.setLength(0);
 178  0
             paramBuffer.append(AJAX_KEY_ONEVENT);
 179  0
             paramBuffer.append(COLON);
 180  0
             paramBuffer.append(onEvent);
 181  0
             onEvent = paramBuffer.toString();
 182  
         }
 183  
         else
 184  
         {
 185  0
             onEvent = null;
 186  
         }
 187  
         /*
 188  
          * since version 2.2
 189  
          */
 190  0
         String delay = behavior.getDelay();
 191  0
         if (delay != null && !delay.trim().equals(EMPTY))
 192  
         {
 193  0
             paramBuffer.setLength(0);
 194  0
             paramBuffer.append(AJAX_KEY_DELAY);
 195  0
             paramBuffer.append(COLON);
 196  0
             if ("none".equals(delay))
 197  
             {
 198  0
                 paramBuffer.append('\'');
 199  0
                 paramBuffer.append(delay);
 200  0
                 paramBuffer.append('\'');
 201  
             }
 202  
             else
 203  
             {
 204  0
                 paramBuffer.append(delay);
 205  
             }
 206  0
             delay = paramBuffer.toString();
 207  
         }
 208  
         else
 209  
         {
 210  0
             delay = null;
 211  
         }
 212  
         /*
 213  
          * since version 2.2
 214  
          */
 215  0
         String resetValues = Boolean.toString(behavior.isResetValues());
 216  0
         if (resetValues.equals("true"))
 217  
         {
 218  0
             paramBuffer.setLength(0);
 219  0
             paramBuffer.append(AJAX_KEY_RESETVALUES);
 220  0
             paramBuffer.append(COLON);
 221  0
             paramBuffer.append(resetValues);
 222  0
             resetValues = paramBuffer.toString();
 223  
         }
 224  
         else
 225  
         {
 226  0
             resetValues = null;
 227  
         }
 228  
 
 229  0
         String sourceId = null;
 230  0
         if (context.getSourceId() == null) 
 231  
         {
 232  0
             sourceId = AJAX_VAL_THIS;
 233  
         }
 234  
         else
 235  
         {
 236  0
             paramBuffer.setLength(0);
 237  0
             paramBuffer.append('\'');
 238  0
             paramBuffer.append(context.getSourceId());
 239  0
             paramBuffer.append('\'');
 240  0
             sourceId = paramBuffer.toString();
 241  
             
 242  0
             if (!context.getSourceId().trim().equals(
 243  
                 context.getComponent().getClientId(context.getFacesContext())))
 244  
             {
 245  
                 // Check if sourceId is not a clientId and there is no execute set
 246  0
                 UIComponent ref = context.getComponent();
 247  0
                 ref = (ref.getParent() == null) ? ref : ref.getParent();
 248  0
                 UIComponent instance = null;
 249  
                 try
 250  
                 {
 251  0
                     instance = ref.findComponent(context.getSourceId());
 252  
                 }
 253  0
                 catch (IllegalArgumentException e)
 254  
                 {
 255  
                     // No Op
 256  0
                 }
 257  0
                 if (instance == null && executes == null)
 258  
                 {
 259  
                     // set the clientId of the component so the behavior can be decoded later, 
 260  
                     // otherwise the behavior will fail
 261  0
                     List<String> list = new ArrayList<String>();
 262  0
                     list.add(context.getComponent().getClientId(context.getFacesContext()));
 263  0
                     executes = mapToString(context, paramBuffer, AJAX_KEY_EXECUTE, list);
 264  
                 }
 265  
             }
 266  
         }
 267  
 
 268  
 
 269  0
         String event = context.getEventName();
 270  
 
 271  0
         retVal.append(JS_AJAX_REQUEST);
 272  0
         retVal.append(L_PAREN);
 273  0
         retVal.append(sourceId);
 274  0
         retVal.append(COMMA);
 275  0
         retVal.append(AJAX_VAL_EVENT);
 276  0
         retVal.append(COMMA);
 277  
 
 278  0
         Collection<ClientBehaviorContext.Parameter> params = context.getParameters();
 279  0
         int paramSize = (params != null) ? params.size() : 0;
 280  
 
 281  0
         List<String> parameterList = new ArrayList<String>(paramSize + 2);
 282  0
         if (executes != null)
 283  
         {
 284  0
             parameterList.add(executes);
 285  
         }
 286  0
         if (render != null)
 287  
         {
 288  0
             parameterList.add(render);
 289  
         }
 290  0
         if (onError != null)
 291  
         {
 292  0
             parameterList.add(onError);
 293  
         }
 294  0
         if (onEvent != null)
 295  
         {
 296  0
             parameterList.add(onEvent);
 297  
         }
 298  
         /*
 299  
          * since version 2.2
 300  
          */
 301  0
         if (delay != null)
 302  
         {
 303  0
             parameterList.add(delay);
 304  
         }
 305  
         /*
 306  
          * since version 2.2
 307  
          */
 308  0
         if (resetValues != null)
 309  
         {
 310  0
             parameterList.add(resetValues);
 311  
         }
 312  0
         if (paramSize > 0)
 313  
         {
 314  
             /**
 315  
              * see ClientBehaviorContext.html of the spec
 316  
              * the param list has to be added in the post back
 317  
              */
 318  
             // params are in 99% RamdonAccess instace created in 
 319  
             // HtmlRendererUtils.getClientBehaviorContextParameters(Map<String, String>)
 320  0
             if (params instanceof RandomAccess)
 321  
             {
 322  0
                 List<ClientBehaviorContext.Parameter> list = (List<ClientBehaviorContext.Parameter>) params;
 323  0
                 for (int i = 0, size = list.size(); i < size; i++)
 324  
                 {
 325  0
                     ClientBehaviorContext.Parameter param = list.get(i);
 326  0
                     append(paramBuffer, parameterList, param);
 327  
                 }
 328  0
             }
 329  
             else
 330  
             {
 331  0
                 for (ClientBehaviorContext.Parameter param : params)
 332  
                 {
 333  0
                     append(paramBuffer, parameterList, param);
 334  0
                 }
 335  
             }
 336  
         }
 337  
 
 338  
         //parameterList.add(QUOTE + BEHAVIOR_EVENT + QUOTE + COLON + QUOTE + event + QUOTE);
 339  0
         paramBuffer.setLength(0);
 340  0
         paramBuffer.append(QUOTE);
 341  0
         paramBuffer.append(BEHAVIOR_EVENT);
 342  0
         paramBuffer.append(QUOTE);
 343  0
         paramBuffer.append(COLON);
 344  0
         paramBuffer.append(QUOTE);
 345  0
         paramBuffer.append(event);
 346  0
         paramBuffer.append(QUOTE);
 347  0
         parameterList.add(paramBuffer.toString());
 348  
         
 349  
         /**
 350  
          * I assume here for now that the options are the same which also
 351  
          * can be sent via the options attribute to javax.faces.ajax
 352  
          * this still needs further clarifications but I assume so for now
 353  
          */
 354  0
         retVal.append(buildOptions(paramBuffer, parameterList));
 355  
 
 356  0
         retVal.append(R_PAREN);
 357  
 
 358  0
         return retVal;
 359  
     }
 360  
 
 361  
     private void append(StringBuilder paramBuffer, List<String> parameterList, ClientBehaviorContext.Parameter param)
 362  
     {
 363  
         //TODO we may need a proper type handling in this part
 364  
         //lets leave it for now as it is
 365  
         //quotes etc.. should be transferred directly
 366  
         //and the rest is up to the toString properly implemented
 367  
         //ANS: Both name and value should be quoted
 368  0
         paramBuffer.setLength(0);
 369  0
         paramBuffer.append(QUOTE);
 370  0
         paramBuffer.append(param.getName());
 371  0
         paramBuffer.append(QUOTE);
 372  0
         paramBuffer.append(COLON);
 373  0
         paramBuffer.append(QUOTE);
 374  0
         paramBuffer.append(param.getValue().toString());
 375  0
         paramBuffer.append(QUOTE);
 376  0
         parameterList.add(paramBuffer.toString());
 377  0
     }
 378  
 
 379  
 
 380  
     private StringBuilder buildOptions(StringBuilder retVal, List<String> options)
 381  
     {
 382  0
         retVal.setLength(0);
 383  
 
 384  0
         retVal.append("{");
 385  
 
 386  0
         boolean first = true;
 387  
 
 388  0
         for (int i = 0, size = options.size(); i < size; i++)
 389  
         {
 390  0
             String option = options.get(i);
 391  0
             if (option != null && !option.trim().equals(EMPTY))
 392  
             {
 393  0
                 if (!first)
 394  
                 {
 395  0
                     retVal.append(COMMA);
 396  
                 }
 397  
                 else
 398  
                 {
 399  0
                     first = false;
 400  
                 }
 401  0
                 retVal.append(option);
 402  
             }
 403  
         }
 404  0
         retVal.append("}");
 405  0
         return retVal;
 406  
     }
 407  
 
 408  
     private final String mapToString(ClientBehaviorContext context, StringBuilder retVal, 
 409  
             String target, Collection<String> dataHolder)
 410  
     {
 411  
         //Clear buffer
 412  0
         retVal.setLength(0);
 413  
 
 414  0
         if (dataHolder == null)
 415  
         {
 416  0
             dataHolder = Collections.emptyList();
 417  
         }
 418  0
         int executeSize = dataHolder.size();
 419  0
         if (executeSize > 0)
 420  
         {
 421  
 
 422  0
             retVal.append(target);
 423  0
             retVal.append(COLON);
 424  0
             retVal.append(QUOTE);
 425  
 
 426  0
             int cnt = 0;
 427  
             
 428  
             // perf: dataHolder is a Collection : ajaxBehaviour.getExecute() 
 429  
             // and ajaxBehaviour.getRender() API
 430  
             // In most cases comes here a ArrayList, because 
 431  
             // javax.faces.component.behavior.AjaxBehavior.getCollectionFromSpaceSplitString
 432  
             // creates it.
 433  0
             if (dataHolder instanceof RandomAccess)
 434  
             {
 435  0
                 List<String> list = (List<String>) dataHolder;
 436  0
                 for (; cnt  < executeSize; cnt++)
 437  
                 {
 438  0
                     String strVal = list.get(cnt);
 439  0
                     build(context, executeSize, retVal, cnt, strVal);
 440  
                 }
 441  0
             }
 442  
             else
 443  
             {
 444  0
                 for (String strVal : dataHolder)
 445  
                 {
 446  0
                     cnt++;
 447  0
                     build(context, executeSize, retVal, cnt, strVal);
 448  0
                 }
 449  
             }
 450  
 
 451  0
             retVal.append(QUOTE);
 452  0
             return retVal.toString();
 453  
         }
 454  0
         return null;
 455  
 
 456  
     }
 457  
 
 458  
     public void build(ClientBehaviorContext context,
 459  
             int size, StringBuilder retVal, int cnt,
 460  
             String strVal)
 461  
     {
 462  0
         strVal = strVal.trim();
 463  0
         if (!EMPTY.equals(strVal))
 464  
         {
 465  0
             if (!strVal.startsWith(IDENTIFYER_MARKER))
 466  
             {
 467  0
                 String componentId = getComponentId(context, strVal);
 468  0
                 retVal.append(componentId);
 469  0
             }
 470  
             else
 471  
             {
 472  
                 // @this should be resolved server side, because there are cases like in h:selectOneRadio where
 473  
                 // the id of the tag is not a clientId. @this could be valid to be resolve in the client if 
 474  
                 // jsf.ajax.request(...) is called manually (without f:ajax intervention)
 475  0
                 if (strVal.equalsIgnoreCase("@this"))
 476  
                 {
 477  0
                     retVal.append(context.getComponent().getClientId(context.getFacesContext()));
 478  
                 }
 479  
                 else
 480  
                 {
 481  0
                     retVal.append(strVal);
 482  
                 }
 483  
             }
 484  0
             if (cnt < size)
 485  
             {
 486  0
                 retVal.append(BLANK);
 487  
             }
 488  
         }
 489  0
     }
 490  
 
 491  
     private final String getComponentId(ClientBehaviorContext context, String id)
 492  
     {
 493  
 
 494  0
         UIComponent contextComponent = context.getComponent();
 495  0
         UIComponent target = contextComponent.findComponent(id);
 496  0
         if (target == null)
 497  
         {
 498  0
             target = contextComponent.findComponent(
 499  
                 context.getFacesContext().getNamingContainerSeparatorChar() + id);
 500  
         }
 501  0
         if (target != null)
 502  
         {
 503  0
             return target.getClientId(context.getFacesContext());
 504  
         }
 505  0
         throw new FacesException("Component with id:" + id + " not found");
 506  
     }
 507  
 
 508  
     private final void assertBehavior(ClientBehavior behavior)
 509  
     {
 510  0
         if (!(behavior instanceof AjaxBehavior))
 511  
         {
 512  0
             throw new FacesException(ERR_NO_AJAX_BEHAVIOR);
 513  
         }
 514  0
     }
 515  
 
 516  
 }