1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.myfaces.shared.renderkit.html;
20
21 import org.apache.myfaces.shared.renderkit.JSFAttr;
22 import org.apache.myfaces.shared.renderkit.RendererUtils;
23 import org.apache.myfaces.shared.renderkit.html.util.FormInfo;
24 import org.apache.myfaces.shared.renderkit.html.util.JavascriptUtils;
25 import org.apache.myfaces.shared.util._ComponentUtils;
26 import org.apache.myfaces.shared.config.MyfacesConfig;
27
28 import javax.faces.application.ViewHandler;
29 import javax.faces.component.UICommand;
30 import javax.faces.component.UIComponent;
31 import javax.faces.component.UIOutput;
32 import javax.faces.component.UIParameter;
33 import javax.faces.component.html.HtmlCommandLink;
34 import javax.faces.context.FacesContext;
35 import javax.faces.context.ResponseWriter;
36 import javax.faces.event.ActionEvent;
37 import java.io.IOException;
38 import java.io.UnsupportedEncodingException;
39 import java.net.URLEncoder;
40 import java.util.Iterator;
41
42 /***
43 * @author Manfred Geiler
44 * @version $Revision: 947903 $ $Date: 2010-05-24 22:40:23 -0500 (Mon, 24 May 2010) $
45 */
46 public abstract class HtmlLinkRendererBase
47 extends HtmlRenderer
48 {
49
50
51
52
53
54
55
56 public boolean getRendersChildren()
57 {
58
59
60 return true;
61 }
62
63 public void decode(FacesContext facesContext, UIComponent component)
64 {
65 super.decode(facesContext, component);
66
67 if (component instanceof UICommand)
68 {
69 String clientId = component.getClientId(facesContext);
70 FormInfo formInfo = findNestingForm(component, facesContext);
71 String reqValue = (String) facesContext.getExternalContext().getRequestParameterMap().get(
72 HtmlRendererUtils.getHiddenCommandLinkFieldName(formInfo));
73 if (reqValue != null && reqValue.equals(clientId))
74 {
75 component.queueEvent(new ActionEvent(component));
76
77 RendererUtils.initPartialValidationAndModelUpdate(component, facesContext);
78 }
79 }
80 else if (component instanceof UIOutput)
81 {
82
83 }
84 else
85 {
86 throw new IllegalArgumentException("Unsupported component class " + component.getClass().getName());
87 }
88 }
89
90
91 public void encodeBegin(FacesContext facesContext, UIComponent component) throws IOException
92 {
93 super.encodeBegin(facesContext, component);
94
95 if (component instanceof UICommand)
96 {
97 renderCommandLinkStart(facesContext, component,
98 component.getClientId(facesContext),
99 ((UICommand)component).getValue(),
100 getStyle(facesContext, component),
101 getStyleClass(facesContext, component));
102 }
103 else if (component instanceof UIOutput)
104 {
105 renderOutputLinkStart(facesContext, (UIOutput)component);
106 }
107 else
108 {
109 throw new IllegalArgumentException("Unsupported component class " + component.getClass().getName());
110 }
111 }
112
113
114 /***
115 * Can be overwritten by derived classes to overrule the style to be used.
116 */
117 protected String getStyle(FacesContext facesContext, UIComponent link)
118 {
119 if (link instanceof HtmlCommandLink)
120 {
121 return ((HtmlCommandLink)link).getStyle();
122 }
123
124 return (String)link.getAttributes().get(HTML.STYLE_ATTR);
125
126 }
127
128 /***
129 * Can be overwritten by derived classes to overrule the style class to be used.
130 */
131 protected String getStyleClass(FacesContext facesContext, UIComponent link)
132 {
133 if (link instanceof HtmlCommandLink)
134 {
135 return ((HtmlCommandLink)link).getStyleClass();
136 }
137
138 return (String)link.getAttributes().get(HTML.STYLE_CLASS_ATTR);
139
140 }
141
142 public void encodeChildren(FacesContext facesContext, UIComponent component) throws IOException
143 {
144 RendererUtils.renderChildren(facesContext, component);
145 }
146
147 public void encodeEnd(FacesContext facesContext, UIComponent component) throws IOException
148 {
149 super.encodeEnd(facesContext, component);
150
151 if (component instanceof UICommand)
152 {
153 renderCommandLinkEnd(facesContext, component);
154
155 HtmlFormRendererBase.renderScrollHiddenInputIfNecessary(
156 findNestingForm(component, facesContext).getForm(), facesContext, facesContext.getResponseWriter());
157 }
158 else if (component instanceof UIOutput)
159 {
160 renderOutputLinkEnd(facesContext, component);
161 }
162 else
163 {
164 throw new IllegalArgumentException("Unsupported component class " + component.getClass().getName());
165 }
166 }
167
168 protected void renderCommandLinkStart(FacesContext facesContext, UIComponent component,
169 String clientId,
170 Object value,
171 String style,
172 String styleClass)
173 throws IOException
174 {
175 ResponseWriter writer = facesContext.getResponseWriter();
176
177 if (HtmlRendererUtils.isDisabled(component))
178 {
179 writer.startElement(HTML.SPAN_ELEM, component);
180 HtmlRendererUtils.writeIdIfNecessary(writer, component, facesContext);
181 HtmlRendererUtils.renderHTMLAttributes(writer, component, HTML.ANCHOR_PASSTHROUGH_ATTRIBUTES);
182 }
183 else
184 {
185 String[] anchorAttrsToRender;
186 if (JavascriptUtils.isJavascriptAllowed(facesContext.getExternalContext()))
187 {
188 renderJavaScriptAnchorStart(facesContext, writer, component, clientId);
189 anchorAttrsToRender = HTML.ANCHOR_PASSTHROUGH_ATTRIBUTES_WITHOUT_ONCLICK_WITHOUT_STYLE;
190 }
191 else
192 {
193 renderNonJavaScriptAnchorStart(facesContext, writer, component, clientId);
194 anchorAttrsToRender = HTML.ANCHOR_PASSTHROUGH_ATTRIBUTES_WITHOUT_STYLE;
195 }
196
197 HtmlRendererUtils.writeIdIfNecessary(writer, component, facesContext);
198 HtmlRendererUtils.renderHTMLAttributes(writer, component,
199 anchorAttrsToRender);
200 HtmlRendererUtils.renderHTMLAttribute(writer, HTML.STYLE_ATTR, HTML.STYLE_ATTR,
201 style);
202 HtmlRendererUtils.renderHTMLAttribute(writer, HTML.STYLE_CLASS_ATTR, HTML.STYLE_CLASS_ATTR,
203 styleClass);
204 }
205
206
207 if(value != null)
208 {
209 writer.writeText(value.toString(), JSFAttr.VALUE_ATTR);
210 }
211 }
212
213 protected void renderJavaScriptAnchorStart(FacesContext facesContext,
214 ResponseWriter writer,
215 UIComponent component,
216 String clientId)
217 throws IOException
218 {
219
220 FormInfo formInfo = findNestingForm(component, facesContext);
221 if (formInfo == null)
222 {
223 String path = RendererUtils.getPathToComponent(component);
224 String msg = "Link is not embedded in a form. Change component/tag '" + clientId + "' from javax.faces.*/<h:tagName /> " +
225 "to org.apache.myfaces.*/<t:tagName />, or embed it in a form. This is not a bug. " +
226 "Please see: http://wiki.apache.org/myfaces/Upgrading_to_Tomahawk_1.1.3 " +
227 "The path to this component is " + path + ". If you need to render a special form and a JSF-form's attributes are not enough," +
228 "consider using the s:form tag of the MyFaces sandbox.";
229 throw new IllegalArgumentException(msg);
230 }
231 UIComponent nestingForm = formInfo.getForm();
232 String formName = formInfo.getFormName();
233
234 StringBuffer onClick = new StringBuffer();
235
236 String commandOnclick;
237 if (component instanceof HtmlCommandLink)
238 {
239 commandOnclick = ((HtmlCommandLink)component).getOnclick();
240 }
241 else
242 {
243 commandOnclick = (String)component.getAttributes().get(HTML.ONCLICK_ATTR);
244 }
245 if (commandOnclick != null)
246 {
247 onClick.append("var cf = function(){");
248 onClick.append(commandOnclick);
249 onClick.append('}');
250 onClick.append(';');
251 onClick.append("var oamSF = function(){");
252 }
253
254 if (RendererUtils.isAdfOrTrinidadForm(formInfo.getForm())) {
255 onClick.append("submitForm('");
256 onClick.append(formInfo.getForm().getClientId(facesContext));
257 onClick.append("',1,{source:'");
258 onClick.append(component.getClientId(facesContext));
259 onClick.append("'});return false;");
260 }
261 else {
262 HtmlRendererUtils.renderFormSubmitScript(facesContext);
263
264 StringBuffer params = addChildParameters(facesContext, component, nestingForm);
265
266 String target = getTarget(component);
267
268 onClick.append("return ").
269 append(HtmlRendererUtils.SUBMIT_FORM_FN_NAME).append("('").
270 append(formName).append("','").
271 append(clientId).append("'");
272
273 if (params.length() > 2 || target != null) {
274 onClick.append(",").
275 append(target == null ? "null" : ("'" + target + "'")).append(",").
276 append(params);
277 }
278 onClick.append(");");
279
280
281
282 if (MyfacesConfig.getCurrentInstance(facesContext.getExternalContext()).isRenderHiddenFieldsForLinkParams())
283 {
284 String hiddenFieldName = HtmlRendererUtils.getHiddenCommandLinkFieldName(formInfo);
285 addHiddenCommandParameter(facesContext, nestingForm, hiddenFieldName);
286 }
287
288 }
289
290 if (commandOnclick != null)
291 {
292 onClick.append('}');
293 onClick.append(';');
294 onClick.append("return (cf()==false)? false : oamSF();");
295 }
296
297 writer.startElement(HTML.ANCHOR_ELEM, component);
298 writer.writeURIAttribute(HTML.HREF_ATTR, "#", null);
299 writer.writeAttribute(HTML.ONCLICK_ATTR, onClick.toString(), null);
300 }
301
302 private String getTarget(UIComponent component) {
303
304 String target;
305 if (component instanceof HtmlCommandLink) {
306 target = ((HtmlCommandLink) component).getTarget();
307 }
308 else {
309 target = (String) component.getAttributes().get(HTML.TARGET_ATTR);
310 }
311 return target;
312 }
313
314 private StringBuffer addChildParameters(FacesContext context, UIComponent component, UIComponent nestingForm) {
315
316 StringBuffer params = new StringBuffer();
317 params.append("[");
318 for (Iterator it = getChildren(component).iterator(); it.hasNext();) {
319
320 UIComponent child = (UIComponent) it.next();
321 if (child instanceof UIParameter) {
322 String name = ((UIParameter) child).getName();
323
324 if (name == null) {
325 throw new IllegalArgumentException("Unnamed parameter value not allowed within command link.");
326 }
327
328
329 if (MyfacesConfig.getCurrentInstance(context.getExternalContext()).isRenderHiddenFieldsForLinkParams())
330 {
331 addHiddenCommandParameter(context, nestingForm, name);
332 }
333
334 Object value = ((UIParameter) child).getValue();
335
336
337
338
339
340
341
342
343
344
345
346
347 String strParamValue = "";
348 if (value != null)
349 {
350 strParamValue = value.toString();
351 StringBuffer buff = null;
352 for (int i = 0; i < strParamValue.length(); i++)
353 {
354 char c = strParamValue.charAt(i);
355 if (c == '\'' || c == '//')
356 {
357 if (buff == null)
358 {
359 buff = new StringBuffer();
360 buff.append(strParamValue.substring(0,i));
361 }
362 buff.append('//');
363 buff.append(c);
364 }
365 else if (buff != null)
366 {
367 buff.append(c);
368 }
369 }
370 if (buff != null)
371 {
372 strParamValue = buff.toString();
373 }
374 }
375
376 if (params.length() > 1) {
377 params.append(",");
378 }
379
380 params.append("['");
381 params.append(name);
382 params.append("','");
383 params.append(strParamValue);
384 params.append("']");
385 }
386 }
387 params.append("]");
388 return params;
389 }
390
391 /***
392 * find nesting form<br />
393 * need to be overrideable to deal with dummyForm stuff in tomahawk.
394 */
395 protected FormInfo findNestingForm(UIComponent uiComponent, FacesContext facesContext)
396 {
397 return _ComponentUtils.findNestingForm(uiComponent, facesContext);
398 }
399
400 protected void addHiddenCommandParameter(FacesContext facesContext, UIComponent nestingForm, String hiddenFieldName)
401 {
402 if (nestingForm != null)
403 {
404 HtmlFormRendererBase.addHiddenCommandParameter(facesContext, nestingForm, hiddenFieldName);
405 }
406 }
407
408
409 protected void renderNonJavaScriptAnchorStart(FacesContext facesContext,
410 ResponseWriter writer,
411 UIComponent component,
412 String clientId)
413 throws IOException
414 {
415 ViewHandler viewHandler = facesContext.getApplication().getViewHandler();
416 String viewId = facesContext.getViewRoot().getViewId();
417 String path = viewHandler.getActionURL(facesContext, viewId);
418
419 boolean strictXhtmlLinks
420 = MyfacesConfig.getCurrentInstance(facesContext.getExternalContext()).isStrictXhtmlLinks();
421
422 StringBuffer hrefBuf = new StringBuffer(path);
423
424
425
426 if (path.indexOf('?') == -1)
427 {
428 hrefBuf.append('?');
429 }
430 else
431 {
432 if (strictXhtmlLinks) {
433 hrefBuf.append("&");
434 } else {
435 hrefBuf.append('&');
436 }
437 }
438 String hiddenFieldName = HtmlRendererUtils.getHiddenCommandLinkFieldName(findNestingForm(component, facesContext));
439 hrefBuf.append(hiddenFieldName);
440 hrefBuf.append('=');
441 hrefBuf.append(clientId);
442
443 if (getChildCount(component) > 0)
444 {
445 addChildParametersToHref(facesContext, component, hrefBuf,
446 false,
447 writer.getCharacterEncoding());
448 }
449
450 String href = facesContext.getExternalContext().encodeActionURL(hrefBuf.toString());
451 writer.startElement(HTML.ANCHOR_ELEM, component);
452 writer.writeURIAttribute(HTML.HREF_ATTR,
453 facesContext.getExternalContext().encodeActionURL(href),
454 null);
455 }
456
457 private void addChildParametersToHref(FacesContext facesContext,
458 UIComponent linkComponent,
459 StringBuffer hrefBuf,
460 boolean firstParameter,
461 String charEncoding)
462 throws IOException
463 {
464 boolean strictXhtmlLinks
465 = MyfacesConfig.getCurrentInstance(facesContext.getExternalContext()).isStrictXhtmlLinks();
466 for (Iterator it = getChildren(linkComponent).iterator(); it.hasNext(); )
467 {
468 UIComponent child = (UIComponent)it.next();
469 if (child instanceof UIParameter)
470 {
471 String name = ((UIParameter)child).getName();
472 Object value = ((UIParameter)child).getValue();
473 addParameterToHref(name, value, hrefBuf, firstParameter, charEncoding, strictXhtmlLinks);
474 firstParameter = false;
475 }
476 }
477 }
478
479 protected void renderOutputLinkStart(FacesContext facesContext, UIOutput output)
480 throws IOException
481 {
482 ResponseWriter writer = facesContext.getResponseWriter();
483
484 if (HtmlRendererUtils.isDisabled(output))
485 {
486 writer.startElement(HTML.SPAN_ELEM, output);
487 HtmlRendererUtils.writeIdIfNecessary(writer, output, facesContext);
488 HtmlRendererUtils.renderHTMLAttributes(writer, output, HTML.ANCHOR_PASSTHROUGH_ATTRIBUTES);
489 }
490 else
491 {
492
493 String href = org.apache.myfaces.shared.renderkit.RendererUtils.getStringValue(facesContext, output);
494
495 int index = href.indexOf('#');
496 String anchorString = null;
497 if (index > -1)
498 {
499
500 anchorString = href.substring(index,href.length());
501 href = href.substring(0,index);
502 }
503 if (getChildCount(output) > 0)
504 {
505 StringBuffer hrefBuf = new StringBuffer(href);
506 addChildParametersToHref(facesContext, output, hrefBuf,
507 (href.indexOf('?') == -1),
508 writer.getCharacterEncoding());
509 href = hrefBuf.toString();
510 }
511 if (index > -1)
512 {
513 href += anchorString;
514 }
515 href = facesContext.getExternalContext().encodeResourceURL(href);
516
517
518 writer.startElement(HTML.ANCHOR_ELEM, output);
519 HtmlRendererUtils.writeIdAndNameIfNecessary(writer, output, facesContext);
520 writer.writeURIAttribute(HTML.HREF_ATTR, href, null);
521 HtmlRendererUtils.renderHTMLAttributes(writer, output, org.apache.myfaces.shared.renderkit.html.HTML.ANCHOR_PASSTHROUGH_ATTRIBUTES);
522 writer.flush();
523 }
524 }
525
526 private void renderLinkParameter(String name,
527 Object value,
528 StringBuffer onClick,
529 String jsForm,
530 UIComponent nestingForm)
531 {
532 if (name == null)
533 {
534 throw new IllegalArgumentException("Unnamed parameter value not allowed within command link.");
535 }
536 onClick.append(jsForm);
537 onClick.append(".elements['").append(name).append("']");
538
539 String strParamValue = value != null ? org.apache.myfaces.shared.renderkit.html.util.HTMLEncoder.encode(value.toString(), false, false) : "";
540 onClick.append(".value='").append(strParamValue).append("';");
541
542 addHiddenCommandParameter(FacesContext.getCurrentInstance(), nestingForm, name);
543 }
544
545 private static void addParameterToHref(String name,
546 Object value,
547 StringBuffer hrefBuf,
548 boolean firstParameter,
549 String charEncoding,
550 boolean strictXhtmlLinks) throws UnsupportedEncodingException {
551 if (name == null) {
552 throw new IllegalArgumentException("Unnamed parameter value not allowed within command link.");
553 }
554
555 if (firstParameter) {
556 hrefBuf.append('?');
557 } else {
558 if (strictXhtmlLinks) {
559 hrefBuf.append("&");
560 } else {
561 hrefBuf.append('&');
562 }
563 }
564
565 hrefBuf.append(URLEncoder.encode(name, charEncoding));
566 hrefBuf.append('=');
567 if (value != null)
568 {
569
570 hrefBuf.append(URLEncoder.encode(value.toString(), charEncoding));
571 }
572 }
573
574
575 protected void renderOutputLinkEnd(FacesContext facesContext, UIComponent component)
576 throws IOException
577 {
578 ResponseWriter writer = facesContext.getResponseWriter();
579
580 if (HtmlRendererUtils.isDisabled(component))
581 {
582 writer.endElement(HTML.SPAN_ELEM);
583 }
584 else
585 {
586
587 writer.writeText("", null);
588 writer.endElement(HTML.ANCHOR_ELEM);
589 }
590 }
591
592 protected void renderCommandLinkEnd(FacesContext facesContext, UIComponent component)
593 throws IOException
594 {
595 ResponseWriter writer = facesContext.getResponseWriter();
596 if (HtmlRendererUtils.isDisabled(component))
597 {
598
599 writer.endElement(HTML.SPAN_ELEM);
600 }
601 else
602 {
603 writer.writeText("", null);
604 writer.endElement(HTML.ANCHOR_ELEM);
605 }
606 }
607
608 }