1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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.EnumSet;
25 import java.util.List;
26 import java.util.RandomAccess;
27 import java.util.Set;
28
29 import javax.faces.FacesException;
30 import javax.faces.component.ActionSource;
31 import javax.faces.component.EditableValueHolder;
32 import javax.faces.component.UIComponent;
33 import javax.faces.component.behavior.AjaxBehavior;
34 import javax.faces.component.behavior.ClientBehavior;
35 import javax.faces.component.behavior.ClientBehaviorContext;
36 import javax.faces.component.search.SearchExpressionContext;
37 import javax.faces.component.search.SearchExpressionHandler;
38 import javax.faces.component.search.SearchExpressionHint;
39 import javax.faces.context.FacesContext;
40 import javax.faces.event.AjaxBehaviorEvent;
41 import javax.faces.event.PhaseId;
42 import javax.faces.render.ClientBehaviorRenderer;
43 import org.apache.myfaces.shared.renderkit.html.util.SharedStringBuilder;
44
45
46
47
48
49 public class HtmlAjaxBehaviorRenderer extends ClientBehaviorRenderer
50 {
51
52 private static final String QUOTE = "'";
53 private static final String BLANK = " ";
54
55 private static final String AJAX_KEY_ONERROR = "onerror";
56 private static final String AJAX_KEY_ONEVENT = "onevent";
57 private static final String AJAX_KEY_EXECUTE = "execute";
58 private static final String AJAX_KEY_RENDER = "render";
59 private static final String AJAX_KEY_DELAY = "delay";
60 private static final String AJAX_KEY_RESETVALUES = "resetValues";
61
62 private static final String AJAX_VAL_THIS = "this";
63 private static final String AJAX_VAL_EVENT = "event";
64 private static final String JS_AJAX_REQUEST = "jsf.ajax.request";
65
66 private static final String COLON = ":";
67 private static final String EMPTY = "";
68 private static final String COMMA = ",";
69
70 private static final String ERR_NO_AJAX_BEHAVIOR = "The behavior must be an instance of AjaxBehavior";
71 private static final String L_PAREN = "(";
72 private static final String R_PAREN = ")";
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 @Override
78 public void decode(FacesContext context, UIComponent component, ClientBehavior behavior)
79 {
80 assertBehavior(behavior);
81 AjaxBehavior ajaxBehavior = (AjaxBehavior) behavior;
82 if (ajaxBehavior.isDisabled() || !component.isRendered())
83 {
84 return;
85 }
86
87 dispatchBehaviorEvent(component, ajaxBehavior);
88 }
89
90 @Override
91 public String getScript(ClientBehaviorContext behaviorContext, ClientBehavior behavior)
92 {
93 assertBehavior(behavior);
94 AjaxBehavior ajaxBehavior = (AjaxBehavior) behavior;
95
96 if (ajaxBehavior.isDisabled())
97 {
98 return null;
99 }
100
101 return makeAjax(behaviorContext, ajaxBehavior).toString();
102 }
103
104 private void dispatchBehaviorEvent(UIComponent component, AjaxBehavior ajaxBehavior)
105 {
106 AjaxBehaviorEvent event = new AjaxBehaviorEvent(component, ajaxBehavior);
107
108 boolean isImmediate = false;
109 if (ajaxBehavior.isImmediateSet())
110 {
111 isImmediate = ajaxBehavior.isImmediate();
112 }
113 else
114 {
115 isImmediate = isComponentImmediate(component);
116 }
117
118 PhaseId phaseId = isImmediate ? PhaseId.APPLY_REQUEST_VALUES : PhaseId.INVOKE_APPLICATION;
119
120 event.setPhaseId(phaseId);
121
122 component.queueEvent(event);
123 }
124
125
126 private boolean isComponentImmediate(UIComponent component)
127 {
128 boolean isImmediate = false;
129 if (component instanceof EditableValueHolder)
130 {
131 isImmediate = ((EditableValueHolder)component).isImmediate();
132 }
133 else if (component instanceof ActionSource)
134 {
135 isImmediate = ((ActionSource)component).isImmediate();
136 }
137 return isImmediate;
138 }
139
140
141
142
143
144
145
146
147
148
149 private StringBuilder makeAjax(ClientBehaviorContext context, AjaxBehavior behavior)
150 {
151 StringBuilder retVal = SharedStringBuilder.get(context.getFacesContext(), AJAX_SB, 60);
152 StringBuilder paramBuffer = SharedStringBuilder.get(context.getFacesContext(), AJAX_PARAM_SB, 20);
153
154 String executes = mapToString(context, paramBuffer, AJAX_KEY_EXECUTE, behavior.getExecute());
155 String render = mapToString(context, paramBuffer, AJAX_KEY_RENDER, behavior.getRender());
156
157 String onError = behavior.getOnerror();
158 if (onError != null && !onError.trim().equals(EMPTY))
159 {
160
161 paramBuffer.setLength(0);
162 paramBuffer.append(AJAX_KEY_ONERROR);
163 paramBuffer.append(COLON);
164 paramBuffer.append(onError);
165 onError = paramBuffer.toString();
166 }
167 else
168 {
169 onError = null;
170 }
171 String onEvent = behavior.getOnevent();
172 if (onEvent != null && !onEvent.trim().equals(EMPTY))
173 {
174 paramBuffer.setLength(0);
175 paramBuffer.append(AJAX_KEY_ONEVENT);
176 paramBuffer.append(COLON);
177 paramBuffer.append(onEvent);
178 onEvent = paramBuffer.toString();
179 }
180 else
181 {
182 onEvent = null;
183 }
184
185
186
187 String delay = behavior.getDelay();
188 if (delay != null && !delay.trim().equals(EMPTY))
189 {
190 paramBuffer.setLength(0);
191 paramBuffer.append(AJAX_KEY_DELAY);
192 paramBuffer.append(COLON);
193 if ("none".equals(delay))
194 {
195 paramBuffer.append('\'');
196 paramBuffer.append(delay);
197 paramBuffer.append('\'');
198 }
199 else
200 {
201 paramBuffer.append(delay);
202 }
203 delay = paramBuffer.toString();
204 }
205 else
206 {
207 delay = null;
208 }
209
210
211
212 String resetValues = Boolean.toString(behavior.isResetValues());
213 if (resetValues.equals("true"))
214 {
215 paramBuffer.setLength(0);
216 paramBuffer.append(AJAX_KEY_RESETVALUES);
217 paramBuffer.append(COLON);
218 paramBuffer.append(resetValues);
219 resetValues = paramBuffer.toString();
220 }
221 else
222 {
223 resetValues = null;
224 }
225
226 String sourceId = null;
227 if (context.getSourceId() == null)
228 {
229 sourceId = AJAX_VAL_THIS;
230 }
231 else
232 {
233 paramBuffer.setLength(0);
234 paramBuffer.append('\'');
235 paramBuffer.append(context.getSourceId());
236 paramBuffer.append('\'');
237 sourceId = paramBuffer.toString();
238
239 if (!context.getSourceId().trim().equals(
240 context.getComponent().getClientId(context.getFacesContext())))
241 {
242
243 UIComponent ref = context.getComponent();
244 ref = (ref.getParent() == null) ? ref : ref.getParent();
245 UIComponent instance = null;
246 try
247 {
248 instance = ref.findComponent(context.getSourceId());
249 }
250 catch (IllegalArgumentException e)
251 {
252
253 }
254 if (instance == null && executes == null)
255 {
256
257
258 List<String> list = new ArrayList<String>();
259 list.add(context.getComponent().getClientId(context.getFacesContext()));
260 executes = mapToString(context, paramBuffer, AJAX_KEY_EXECUTE, list);
261 }
262 }
263 }
264
265
266 String event = context.getEventName();
267
268 retVal.append(JS_AJAX_REQUEST);
269 retVal.append(L_PAREN);
270 retVal.append(sourceId);
271 retVal.append(COMMA);
272 retVal.append(AJAX_VAL_EVENT);
273 retVal.append(COMMA);
274
275 Collection<ClientBehaviorContext.Parameter> params = context.getParameters();
276 int paramSize = (params != null) ? params.size() : 0;
277
278 List<String> parameterList = new ArrayList<>(paramSize + 2);
279 if (executes != null)
280 {
281 parameterList.add(executes);
282 }
283 if (render != null)
284 {
285 parameterList.add(render);
286 }
287 if (onError != null)
288 {
289 parameterList.add(onError);
290 }
291 if (onEvent != null)
292 {
293 parameterList.add(onEvent);
294 }
295
296
297
298 if (delay != null)
299 {
300 parameterList.add(delay);
301 }
302
303
304
305 if (resetValues != null)
306 {
307 parameterList.add(resetValues);
308 }
309 if (paramSize > 0)
310 {
311
312
313
314
315
316
317 if (params instanceof RandomAccess)
318 {
319 List<ClientBehaviorContext.Parameter> list = (List<ClientBehaviorContext.Parameter>) params;
320 for (int i = 0, size = list.size(); i < size; i++)
321 {
322 ClientBehaviorContext.Parameter param = list.get(i);
323 append(paramBuffer, parameterList, param);
324 }
325 }
326 else
327 {
328 for (ClientBehaviorContext.Parameter param : params)
329 {
330 append(paramBuffer, parameterList, param);
331 }
332 }
333 }
334
335
336 paramBuffer.setLength(0);
337 paramBuffer.append(QUOTE);
338 paramBuffer.append(ClientBehaviorContext.BEHAVIOR_EVENT_PARAM_NAME);
339 paramBuffer.append(QUOTE);
340 paramBuffer.append(COLON);
341 paramBuffer.append(QUOTE);
342 paramBuffer.append(event);
343 paramBuffer.append(QUOTE);
344 parameterList.add(paramBuffer.toString());
345
346
347
348
349
350
351 retVal.append(buildOptions(paramBuffer, parameterList));
352
353 retVal.append(R_PAREN);
354
355 return retVal;
356 }
357
358 private void append(StringBuilder paramBuffer, List<String> parameterList, ClientBehaviorContext.Parameter param)
359 {
360
361
362
363
364
365 paramBuffer.setLength(0);
366 paramBuffer.append(QUOTE);
367 paramBuffer.append(param.getName());
368 paramBuffer.append(QUOTE);
369 paramBuffer.append(COLON);
370 paramBuffer.append(QUOTE);
371 paramBuffer.append(param.getValue().toString());
372 paramBuffer.append(QUOTE);
373 parameterList.add(paramBuffer.toString());
374 }
375
376
377 private StringBuilder buildOptions(StringBuilder retVal, List<String> options)
378 {
379 retVal.setLength(0);
380
381 retVal.append("{");
382
383 boolean first = true;
384
385 for (int i = 0, size = options.size(); i < size; i++)
386 {
387 String option = options.get(i);
388 if (option != null && !option.trim().equals(EMPTY))
389 {
390 if (!first)
391 {
392 retVal.append(COMMA);
393 }
394 else
395 {
396 first = false;
397 }
398 retVal.append(option);
399 }
400 }
401 retVal.append("}");
402 return retVal;
403 }
404
405 private String mapToString(ClientBehaviorContext context, StringBuilder retVal,
406 String target, Collection<String> dataHolder)
407 {
408
409 retVal.setLength(0);
410
411 if (dataHolder == null)
412 {
413 dataHolder = Collections.emptyList();
414 }
415 int executeSize = dataHolder.size();
416 if (executeSize > 0)
417 {
418
419 retVal.append(target);
420 retVal.append(COLON);
421 retVal.append(QUOTE);
422
423 int cnt = 0;
424
425 SearchExpressionContext searchExpressionContext = null;
426
427
428
429
430
431
432 if (dataHolder instanceof RandomAccess)
433 {
434 List<String> list = (List<String>) dataHolder;
435 for (; cnt < executeSize; cnt++)
436 {
437 if (searchExpressionContext == null)
438 {
439 searchExpressionContext = SearchExpressionContext.createSearchExpressionContext(
440 context.getFacesContext(), context.getComponent(), EXPRESSION_HINTS, null);
441 }
442
443 String strVal = list.get(cnt);
444 build(context, executeSize, retVal, cnt, strVal, searchExpressionContext);
445 }
446 }
447 else
448 {
449 for (String strVal : dataHolder)
450 {
451 if (searchExpressionContext == null)
452 {
453 searchExpressionContext = SearchExpressionContext.createSearchExpressionContext(
454 context.getFacesContext(), context.getComponent(), EXPRESSION_HINTS, null);
455 }
456
457 cnt++;
458 build(context, executeSize, retVal, cnt, strVal, searchExpressionContext);
459 }
460 }
461
462 retVal.append(QUOTE);
463 return retVal.toString();
464 }
465 return null;
466
467 }
468
469 private static final Set<SearchExpressionHint> EXPRESSION_HINTS =
470 EnumSet.of(SearchExpressionHint.RESOLVE_CLIENT_SIDE, SearchExpressionHint.RESOLVE_SINGLE_COMPONENT);
471
472 public void build(ClientBehaviorContext context,
473 int size, StringBuilder retVal, int cnt,
474 String strVal, SearchExpressionContext searchExpressionContext)
475 {
476 strVal = strVal.trim();
477 if (!EMPTY.equals(strVal))
478 {
479
480
481
482
483
484
485
486
487
488
489 SearchExpressionHandler handler = context.getFacesContext().getApplication().getSearchExpressionHandler();
490 String clientId = handler.resolveClientId(searchExpressionContext, strVal);
491 retVal.append(clientId);
492 if (cnt < size)
493 {
494 retVal.append(BLANK);
495 }
496 }
497 }
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518 private void assertBehavior(ClientBehavior behavior)
519 {
520 if (!(behavior instanceof AjaxBehavior))
521 {
522 throw new FacesException(ERR_NO_AJAX_BEHAVIOR);
523 }
524 }
525
526 }