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