View Javadoc

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.shared.renderkit.html;
20  
21  import java.io.IOException;
22  import java.io.UnsupportedEncodingException;
23  import java.net.URLEncoder;
24  import java.util.Collection;
25  import java.util.Collections;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.RandomAccess;
29  
30  import javax.faces.application.ViewHandler;
31  import javax.faces.component.UICommand;
32  import javax.faces.component.UIComponent;
33  import javax.faces.component.UIOutcomeTarget;
34  import javax.faces.component.UIOutput;
35  import javax.faces.component.UIParameter;
36  import javax.faces.component.behavior.ClientBehavior;
37  import javax.faces.component.behavior.ClientBehaviorContext;
38  import javax.faces.component.behavior.ClientBehaviorHint;
39  import javax.faces.component.behavior.ClientBehaviorHolder;
40  import javax.faces.component.html.HtmlCommandLink;
41  import javax.faces.component.html.HtmlOutputLink;
42  import javax.faces.context.FacesContext;
43  import javax.faces.context.ResponseWriter;
44  import javax.faces.event.ActionEvent;
45  
46  import org.apache.myfaces.shared.config.MyfacesConfig;
47  import org.apache.myfaces.shared.renderkit.ClientBehaviorEvents;
48  import org.apache.myfaces.shared.renderkit.JSFAttr;
49  import org.apache.myfaces.shared.renderkit.RendererUtils;
50  import org.apache.myfaces.shared.renderkit.html.util.FormInfo;
51  import org.apache.myfaces.shared.renderkit.html.util.JavascriptUtils;
52  import org.apache.myfaces.shared.renderkit.html.util.ResourceUtils;
53  import org.apache.myfaces.shared.util._ComponentUtils;
54  
55  /**
56   * @author Manfred Geiler
57   * @version $Revision: 1364643 $ $Date: 2012-07-23 09:25:19 -0500 (Mon, 23 Jul 2012) $
58   */
59  public abstract class HtmlLinkRendererBase
60      extends HtmlRenderer
61  {
62      /* this one is never used
63      public static final String URL_STATE_MARKER      = "JSF_URL_STATE_MARKER=DUMMY";
64      public static final int    URL_STATE_MARKER_LEN  = URL_STATE_MARKER.length();
65      */
66  
67      //private static final Log log = LogFactory.getLog(HtmlLinkRenderer.class);
68      
69      public static final String END_LINK_OUTCOME_AS_SPAN = 
70          "oam.shared.HtmlLinkRendererBase.END_LINK_OUTCOME_AS_SPAN";
71  
72      public boolean getRendersChildren()
73      {
74          // We must be able to render the children without a surrounding anchor
75          // if the Link is disabled
76          return true;
77      }
78  
79      public void decode(FacesContext facesContext, UIComponent component)
80      {
81          super.decode(facesContext, component);  //check for NP
82  
83          if (component instanceof UICommand)
84          {
85              String clientId = component.getClientId(facesContext);
86              FormInfo formInfo = findNestingForm(component, facesContext);
87              if (formInfo != null)
88              {
89                  String reqValue = (String) facesContext.getExternalContext().getRequestParameterMap().get(
90                          HtmlRendererUtils.getHiddenCommandLinkFieldName(formInfo, facesContext));
91                  if (reqValue != null && reqValue.equals(clientId)
92                      || HtmlRendererUtils.isPartialOrBehaviorSubmit(facesContext, clientId))
93                  {
94                      component.queueEvent(new ActionEvent(component));
95  
96                      RendererUtils.initPartialValidationAndModelUpdate(component, facesContext);
97                  }
98              }
99              if (component instanceof ClientBehaviorHolder &&
100                     !HtmlRendererUtils.isDisabled(component))
101             {
102                 HtmlRendererUtils.decodeClientBehaviors(facesContext, component);
103             }
104         }
105         else if (component instanceof UIOutput)
106         {
107             //do nothing
108             if (component instanceof ClientBehaviorHolder &&
109                     !HtmlRendererUtils.isDisabled(component))
110             {
111                 HtmlRendererUtils.decodeClientBehaviors(facesContext, component);
112             }
113         }
114         else
115         {
116             throw new IllegalArgumentException("Unsupported component class " + component.getClass().getName());
117         }
118     }
119 
120 
121     public void encodeBegin(FacesContext facesContext, UIComponent component) throws IOException
122     {
123         super.encodeBegin(facesContext, component);  //check for NP
124 
125         Map<String, List<ClientBehavior>> behaviors = null;
126         if (component instanceof ClientBehaviorHolder)
127         {
128             behaviors = ((ClientBehaviorHolder) component).getClientBehaviors();
129             if (!behaviors.isEmpty())
130             {
131                 ResourceUtils.renderDefaultJsfJsInlineIfNecessary(facesContext, facesContext.getResponseWriter());
132             }
133         }
134         
135         if (component instanceof UICommand)
136         {
137             renderCommandLinkStart(facesContext, component,
138                                    component.getClientId(facesContext),
139                                    ((UICommand)component).getValue(),
140                                    getStyle(facesContext, component),
141                                    getStyleClass(facesContext, component));
142         }
143         else if (component instanceof UIOutcomeTarget)
144         {
145             renderOutcomeLinkStart(facesContext, (UIOutcomeTarget)component);
146         }        
147         else if (component instanceof UIOutput)
148         {
149             renderOutputLinkStart(facesContext, (UIOutput)component);
150         }
151         else
152         {
153             throw new IllegalArgumentException("Unsupported component class " + component.getClass().getName());
154         }
155     }
156 
157 
158     /**
159      * Can be overwritten by derived classes to overrule the style to be used.
160      */
161     protected String getStyle(FacesContext facesContext, UIComponent link)
162     {
163         if (link instanceof HtmlCommandLink)
164         {
165             return ((HtmlCommandLink)link).getStyle();
166         }
167 
168         return (String)link.getAttributes().get(HTML.STYLE_ATTR);
169 
170     }
171 
172     /**
173      * Can be overwritten by derived classes to overrule the style class to be used.
174      */
175     protected String getStyleClass(FacesContext facesContext, UIComponent link)
176     {
177         if (link instanceof HtmlCommandLink)
178         {
179             return ((HtmlCommandLink)link).getStyleClass();
180         }
181 
182         return (String)link.getAttributes().get(HTML.STYLE_CLASS_ATTR);
183 
184     }
185 
186     public void encodeChildren(FacesContext facesContext, UIComponent component) throws IOException
187     {
188         RendererUtils.renderChildren(facesContext, component);
189     }
190 
191     public void encodeEnd(FacesContext facesContext, UIComponent component) throws IOException
192     {
193         super.encodeEnd(facesContext, component);  //check for NP
194 
195         if (component instanceof UICommand)
196         {
197             renderCommandLinkEnd(facesContext, component);
198 
199             FormInfo formInfo = findNestingForm(component, facesContext);
200             
201             if (formInfo != null)
202             {
203                 HtmlFormRendererBase.renderScrollHiddenInputIfNecessary(
204                         formInfo.getForm(), facesContext, facesContext.getResponseWriter());
205             }
206         }
207         else if (component instanceof UIOutcomeTarget)
208         {
209             renderOutcomeLinkEnd(facesContext, component);
210         }
211         else if (component instanceof UIOutput)
212         {
213             renderOutputLinkEnd(facesContext, component);
214         }
215         else
216         {
217             throw new IllegalArgumentException("Unsupported component class " + component.getClass().getName());
218         }
219     }
220 
221     protected void renderCommandLinkStart(FacesContext facesContext, UIComponent component,
222                                           String clientId,
223                                           Object value,
224                                           String style,
225                                           String styleClass)
226             throws IOException
227     {
228         ResponseWriter writer = facesContext.getResponseWriter();
229         Map<String, List<ClientBehavior>> behaviors = null;
230 
231         // h:commandLink can be rendered outside a form, but with warning (jsf 2.0 TCK)
232         FormInfo formInfo = findNestingForm(component, facesContext);
233         
234         if (HtmlRendererUtils.isDisabled(component) || formInfo == null)
235         {
236             writer.startElement(HTML.SPAN_ELEM, component);
237             if (component instanceof ClientBehaviorHolder && JavascriptUtils.isJavascriptAllowed(
238                     facesContext.getExternalContext()))
239             {
240                 behaviors = ((ClientBehaviorHolder) component).getClientBehaviors();
241                 if (!behaviors.isEmpty())
242                 {
243                     HtmlRendererUtils.writeIdAndName(writer, component, facesContext);
244                 }
245                 else
246                 {
247                     HtmlRendererUtils.writeIdIfNecessary(writer, component, facesContext);
248                 }
249                 long commonPropertiesMarked = 0L;
250                 if (isCommonPropertiesOptimizationEnabled(facesContext))
251                 {
252                     commonPropertiesMarked = CommonPropertyUtils.getCommonPropertiesMarked(component);
253                 }
254                 if (behaviors.isEmpty() && isCommonPropertiesOptimizationEnabled(facesContext))
255                 {
256                     CommonPropertyUtils.renderEventProperties(writer, 
257                             commonPropertiesMarked, component);
258                     CommonPropertyUtils.renderFocusBlurEventProperties(writer,
259                             commonPropertiesMarked, component);
260                 }
261                 else
262                 {
263                     if (isCommonEventsOptimizationEnabled(facesContext))
264                     {
265                         Long commonEventsMarked = CommonEventUtils.getCommonEventsMarked(component);
266                         CommonEventUtils.renderBehaviorizedEventHandlers(facesContext, writer, 
267                                 commonPropertiesMarked, commonEventsMarked, component, behaviors);
268                         CommonEventUtils.renderBehaviorizedFieldEventHandlersWithoutOnchangeAndOnselect(
269                             facesContext, writer, commonPropertiesMarked, commonEventsMarked, component, behaviors);
270                     }
271                     else
272                     {
273                         HtmlRendererUtils.renderBehaviorizedEventHandlers(facesContext, writer, component, behaviors);
274                         HtmlRendererUtils.renderBehaviorizedFieldEventHandlersWithoutOnchangeAndOnselect(
275                                 facesContext, writer, component, behaviors);
276                     }
277                 }
278                 if (isCommonPropertiesOptimizationEnabled(facesContext))
279                 {
280                     CommonPropertyUtils.renderAnchorPassthroughPropertiesWithoutEvents(writer, 
281                             commonPropertiesMarked, component);
282                 }
283                 else
284                 {
285                     HtmlRendererUtils.renderHTMLAttributes(writer, component, 
286                             HTML.ANCHOR_PASSTHROUGH_ATTRIBUTES_WITHOUT_EVENTS);
287                 }
288             }
289             else
290             {
291                 HtmlRendererUtils.writeIdIfNecessary(writer, component, facesContext);
292                 if (isCommonPropertiesOptimizationEnabled(facesContext))
293                 {
294                     CommonPropertyUtils.renderAnchorPassthroughProperties(writer, 
295                             CommonPropertyUtils.getCommonPropertiesMarked(component), component);
296                 }
297                 else
298                 {
299                     HtmlRendererUtils.renderHTMLAttributes(writer, component, HTML.ANCHOR_PASSTHROUGH_ATTRIBUTES);
300                 }
301             }
302         }
303         else
304         {
305             //String[] anchorAttrsToRender;
306             if (JavascriptUtils.isJavascriptAllowed(facesContext.getExternalContext()))
307             {
308                 if (component instanceof ClientBehaviorHolder)
309                 {
310                     behaviors = ((ClientBehaviorHolder) component).getClientBehaviors();
311                     renderBehaviorizedJavaScriptAnchorStart(
312                             facesContext, writer, component, clientId, behaviors, formInfo);
313                     if (!behaviors.isEmpty())
314                     {
315                         HtmlRendererUtils.writeIdAndName(writer, component, facesContext);
316                     }
317                     else 
318                     {
319                         // If onclick is not null, both onclick and server side script are rendered 
320                         // using jsf.util.chain(...) js function. We need to check that case and force
321                         // id/name rendering. It is possible to do something else in that case and 
322                         // do not render the script using jsf.util.chain, but for now it is ok.
323                         String commandOnclick;
324                         if (component instanceof HtmlCommandLink)
325                         {
326                             commandOnclick = ((HtmlCommandLink)component).getOnclick();
327                         }
328                         else
329                         {
330                             commandOnclick = (String)component.getAttributes().get(HTML.ONCLICK_ATTR);
331                         }
332                         
333                         if (commandOnclick != null)
334                         {
335                             HtmlRendererUtils.writeIdAndName(writer, component, facesContext);
336                         }
337                         else
338                         {
339                             HtmlRendererUtils.writeIdIfNecessary(writer, component, facesContext);
340                         }
341                     }
342                     long commonPropertiesMarked = 0L;
343                     if (isCommonPropertiesOptimizationEnabled(facesContext))
344                     {
345                         commonPropertiesMarked = CommonPropertyUtils.getCommonPropertiesMarked(component);
346                     }
347                     if (behaviors.isEmpty() && isCommonPropertiesOptimizationEnabled(facesContext))
348                     {
349                         CommonPropertyUtils.renderEventPropertiesWithoutOnclick(writer,
350                             commonPropertiesMarked, component);
351                         CommonPropertyUtils.renderFocusBlurEventProperties(writer,
352                                 commonPropertiesMarked, component);
353                     }
354                     else
355                     {
356                         HtmlRendererUtils.renderBehaviorizedEventHandlersWithoutOnclick(
357                                 facesContext, writer, component, behaviors);
358                         HtmlRendererUtils.renderBehaviorizedFieldEventHandlersWithoutOnchangeAndOnselect(
359                                 facesContext, writer, component, behaviors);
360                     }
361                     if (isCommonPropertiesOptimizationEnabled(facesContext))
362                     {
363                         CommonPropertyUtils.renderAnchorPassthroughPropertiesWithoutStyleAndEvents(writer, 
364                                 commonPropertiesMarked, component);
365                     }
366                     else
367                     {
368                         HtmlRendererUtils.renderHTMLAttributes(writer, component, 
369                                 HTML.ANCHOR_PASSTHROUGH_ATTRIBUTES_WITHOUT_STYLE_AND_EVENTS);
370                     }
371                 }
372                 else
373                 {
374                     renderJavaScriptAnchorStart(facesContext, writer, component, clientId, formInfo);
375                     HtmlRendererUtils.writeIdIfNecessary(writer, component, facesContext);
376                     if (isCommonPropertiesOptimizationEnabled(facesContext))
377                     {
378                         CommonPropertyUtils.renderAnchorPassthroughPropertiesWithoutOnclickAndStyle(writer, 
379                                 CommonPropertyUtils.getCommonPropertiesMarked(component), component);
380                     }
381                     else
382                     {
383                         HtmlRendererUtils.renderHTMLAttributes(writer, component, 
384                                 HTML.ANCHOR_PASSTHROUGH_ATTRIBUTES_WITHOUT_ONCLICK_WITHOUT_STYLE);
385                     }
386                 }
387             }
388             else
389             {
390                 renderNonJavaScriptAnchorStart(facesContext, writer, component, clientId, formInfo);
391                 HtmlRendererUtils.writeIdIfNecessary(writer, component, facesContext);
392                 if (isCommonPropertiesOptimizationEnabled(facesContext))
393                 {
394                     CommonPropertyUtils.renderAnchorPassthroughPropertiesWithoutStyle(writer, 
395                             CommonPropertyUtils.getCommonPropertiesMarked(component), component);
396                 }
397                 else
398                 {
399                     HtmlRendererUtils.renderHTMLAttributes(writer, component, 
400                             HTML.ANCHOR_PASSTHROUGH_ATTRIBUTES_WITHOUT_STYLE);
401                 }
402             }
403 
404             //HtmlRendererUtils.renderHTMLAttributes(writer, component,
405             //                                       anchorAttrsToRender);
406             HtmlRendererUtils.renderHTMLAttribute(writer, HTML.STYLE_ATTR, HTML.STYLE_ATTR,
407                                                   style);
408             HtmlRendererUtils.renderHTMLAttribute(writer, HTML.STYLE_CLASS_ATTR, HTML.STYLE_CLASS_ATTR,
409                                                   styleClass);
410         }
411 
412         // render value as required by JSF 1.1 renderkitdocs
413         if(value != null)
414         {
415             writer.writeText(value.toString(), JSFAttr.VALUE_ATTR);
416         }
417         
418         // render warning message for a h:commandLink with no nesting form
419         if (formInfo == null)
420         {
421             writer.writeText(": This link is deactivated, because it is not embedded in a JSF form.", null);
422         }
423     }
424 
425     protected void renderJavaScriptAnchorStart(FacesContext facesContext,
426                                                ResponseWriter writer,
427                                                UIComponent component,
428                                                String clientId,
429                                                FormInfo formInfo)
430         throws IOException
431     {
432         UIComponent nestingForm = formInfo.getForm();
433         String formName = formInfo.getFormName();
434 
435         StringBuilder onClick = new StringBuilder();
436 
437         String commandOnclick;
438         if (component instanceof HtmlCommandLink)
439         {
440             commandOnclick = ((HtmlCommandLink)component).getOnclick();
441         }
442         else
443         {
444             commandOnclick = (String)component.getAttributes().get(HTML.ONCLICK_ATTR);
445         }
446         if (commandOnclick != null)
447         {
448             onClick.append("var cf = function(){");
449             onClick.append(commandOnclick);
450             onClick.append('}');
451             onClick.append(';');
452             onClick.append("var oamSF = function(){");
453         }
454 
455         if (RendererUtils.isAdfOrTrinidadForm(formInfo.getForm()))
456         {
457             onClick.append("submitForm('");
458             onClick.append(formInfo.getForm().getClientId(facesContext));
459             onClick.append("',1,{source:'");
460             onClick.append(component.getClientId(facesContext));
461             onClick.append("'});return false;");
462         }
463         else
464         {
465             HtmlRendererUtils.renderFormSubmitScript(facesContext);
466 
467             StringBuilder params = addChildParameters(facesContext, component, nestingForm);
468 
469             String target = getTarget(component);
470 
471             if (MyfacesConfig.getCurrentInstance(facesContext.getExternalContext()).isRenderFormSubmitScriptInline())
472             {
473                 onClick.append("return ").
474                     append(HtmlRendererUtils.SUBMIT_FORM_FN_NAME).append("('").
475                     append(formName).append("','").
476                     append(clientId).append("'");
477             }
478             else
479             {
480                 onClick.append("return ").
481                     append(HtmlRendererUtils.SUBMIT_FORM_FN_NAME_JSF2).append("('").
482                     append(formName).append("','").
483                     append(clientId).append("'");
484             }
485 
486             if (params.length() > 2 || target != null)
487             {
488                 onClick.append(",").
489                     append(target == null ? "null" : ("'" + target + "'")).append(",").
490                     append(params);
491             }
492             onClick.append(");");
493 
494             //Not necessary since we are using oamSetHiddenInput to create input hidden fields
495             //render hidden field - todo: in here for backwards compatibility
496             if (MyfacesConfig.getCurrentInstance(
497                     facesContext.getExternalContext()).isRenderHiddenFieldsForLinkParams())
498             {
499                 String hiddenFieldName = HtmlRendererUtils.getHiddenCommandLinkFieldName(
500                         formInfo, facesContext);
501                 addHiddenCommandParameter(facesContext, nestingForm, hiddenFieldName);
502             }
503 
504         }
505         
506         if (commandOnclick != null)
507         {
508             onClick.append('}');
509             onClick.append(';');
510             onClick.append("return (cf.apply(this, [])==false)? false : oamSF.apply(this, []); ");
511         }        
512 
513         writer.startElement(HTML.ANCHOR_ELEM, component);
514         writer.writeURIAttribute(HTML.HREF_ATTR, "#", null);
515         writer.writeAttribute(HTML.ONCLICK_ATTR, onClick.toString(), null);
516     }
517 
518     
519     protected void renderBehaviorizedJavaScriptAnchorStart(FacesContext facesContext,
520             ResponseWriter writer,
521             UIComponent component,
522             String clientId,
523             Map<String, List<ClientBehavior>> behaviors,
524             FormInfo formInfo)
525     throws IOException
526     {
527         String commandOnclick;
528         if (component instanceof HtmlCommandLink)
529         {
530             commandOnclick = ((HtmlCommandLink)component).getOnclick();
531         }
532         else
533         {
534             commandOnclick = (String)component.getAttributes().get(HTML.ONCLICK_ATTR);
535         }
536 
537         //Calculate the script necessary to submit form
538         String serverEventCode = buildServerOnclick(facesContext, component, clientId, formInfo);
539         
540         String onclick = null;
541         
542         if (commandOnclick == null && (behaviors.isEmpty() || 
543             (!behaviors.containsKey(ClientBehaviorEvents.CLICK) && 
544              !behaviors.containsKey(ClientBehaviorEvents.ACTION) ) ) )
545         {
546             //we need to render only the submit script
547             onclick = serverEventCode;
548         }
549         else
550         {
551             boolean hasSubmittingBehavior = hasSubmittingBehavior(behaviors, ClientBehaviorEvents.CLICK)
552                 || hasSubmittingBehavior(behaviors, ClientBehaviorEvents.ACTION);
553             if (!hasSubmittingBehavior)
554             {
555                 //Ensure required resource javascript is available
556                 ResourceUtils.renderDefaultJsfJsInlineIfNecessary(facesContext, writer);
557             }
558             
559             //render a javascript that chain the related code
560             Collection<ClientBehaviorContext.Parameter> paramList = 
561                 HtmlRendererUtils.getClientBehaviorContextParameters(
562                     HtmlRendererUtils.mapAttachedParamsToStringValues(facesContext, component));
563             
564             onclick = HtmlRendererUtils.buildBehaviorChain(facesContext, component,
565                     ClientBehaviorEvents.CLICK, paramList, ClientBehaviorEvents.ACTION, paramList, behaviors,
566                     commandOnclick , hasSubmittingBehavior ? null : serverEventCode);
567         }
568         
569         writer.startElement(HTML.ANCHOR_ELEM, component);
570         writer.writeURIAttribute(HTML.HREF_ATTR, "#", null);
571         writer.writeAttribute(HTML.ONCLICK_ATTR, onclick, null);
572     }
573 
574     private boolean hasSubmittingBehavior(Map<String, List<ClientBehavior>> clientBehaviors, String eventName)
575     {
576         List<ClientBehavior> eventBehaviors = clientBehaviors.get(eventName);
577         if (eventBehaviors != null && !eventBehaviors.isEmpty())
578         {
579             // perf: in 99% cases is  eventBehaviors javax.faces.component._DeltaList._DeltaList(int) = RandomAccess
580             // instance created in javax.faces.component.UIComponentBase.addClientBehavior(String, ClientBehavior), but
581             // component libraries can provide own implementation
582             if (eventBehaviors instanceof RandomAccess)
583             {
584                 for (int i = 0, size = eventBehaviors.size(); i < size; i++)
585                 {
586                     ClientBehavior behavior = eventBehaviors.get(i);
587                     if (behavior.getHints().contains(ClientBehaviorHint.SUBMITTING))
588                     {
589                         return true;
590                     }
591                 }
592             }
593             else
594             {
595                 for (ClientBehavior behavior : eventBehaviors)
596                 {
597                     if (behavior.getHints().contains(ClientBehaviorHint.SUBMITTING))
598                     {
599                         return true;
600                     }
601                 }
602             }
603         }
604         return false;
605     }
606 
607     protected String buildServerOnclick(FacesContext facesContext, UIComponent component, 
608             String clientId, FormInfo formInfo) throws IOException
609     {
610         UIComponent nestingForm = formInfo.getForm();
611         String formName = formInfo.getFormName();
612 
613         StringBuilder onClick = new StringBuilder();
614 
615         if (RendererUtils.isAdfOrTrinidadForm(formInfo.getForm()))
616         {
617             onClick.append("submitForm('");
618             onClick.append(formInfo.getForm().getClientId(facesContext));
619             onClick.append("',1,{source:'");
620             onClick.append(component.getClientId(facesContext));
621             onClick.append("'});return false;");
622         }
623         else
624         {
625             HtmlRendererUtils.renderFormSubmitScript(facesContext);
626 
627             StringBuilder params = addChildParameters(facesContext, component, nestingForm);
628 
629             String target = getTarget(component);
630 
631             if (MyfacesConfig.getCurrentInstance(
632                     facesContext.getExternalContext()).isRenderFormSubmitScriptInline())
633             {
634                 onClick.append("return ").
635                     append(HtmlRendererUtils.SUBMIT_FORM_FN_NAME).append("('").
636                     append(formName).append("','").
637                     append(clientId).append("'");
638             }
639             else
640             {
641                 onClick.append("return ").
642                     append(HtmlRendererUtils.SUBMIT_FORM_FN_NAME_JSF2).append("('").
643                     append(formName).append("','").
644                     append(clientId).append("'");
645             }
646 
647             if (params.length() > 2 || target != null)
648             {
649                 onClick.append(",").
650                     append(target == null ? "null" : ("'" + target + "'")).append(",").
651                     append(params);
652             }
653             onClick.append(");");
654 
655             //Not necessary since we are using oamSetHiddenInput to create input hidden fields
656             //render hidden field - todo: in here for backwards compatibility
657             //String hiddenFieldName = HtmlRendererUtils.getHiddenCommandLinkFieldName(formInfo);
658             //addHiddenCommandParameter(facesContext, nestingForm, hiddenFieldName);
659 
660         }
661         return onClick.toString();
662     }
663 
664     private String getTarget(UIComponent component)
665     {
666         // for performance reason: double check for the target attribute
667         String target;
668         if (component instanceof HtmlCommandLink)
669         {
670             target = ((HtmlCommandLink) component).getTarget();
671         }
672         else
673         {
674             target = (String) component.getAttributes().get(HTML.TARGET_ATTR);
675         }
676         return target;
677     }
678 
679     private StringBuilder addChildParameters(FacesContext context, UIComponent component, UIComponent nestingForm)
680     {
681         //add child parameters
682         StringBuilder params = new StringBuilder();
683         params.append("[");
684         
685         List<UIComponent> childrenList = null;
686         if (getChildCount(component) > 0)
687         {
688             childrenList = getChildren(component);
689         }
690         else
691         {
692            childrenList = Collections.emptyList();
693         }
694         List<UIParameter> validParams = HtmlRendererUtils.getValidUIParameterChildren(
695                 context, childrenList, false, false);
696         for (int j = 0, size = validParams.size(); j < size; j++) 
697         {
698             UIParameter param = validParams.get(j);
699             String name = param.getName();
700 
701             //Not necessary, since we are using oamSetHiddenInput to create hidden fields
702             if (MyfacesConfig.getCurrentInstance(context.getExternalContext()).isRenderHiddenFieldsForLinkParams())
703             {
704                 addHiddenCommandParameter(context, nestingForm, name);
705             }
706 
707             Object value = param.getValue();
708 
709             //UIParameter is no ValueHolder, so no conversion possible - calling .toString on value....
710             // MYFACES-1832 bad charset encoding for f:param
711             // if HTMLEncoder.encode is called, then
712             // when is called on writer.writeAttribute, encode method
713             // is called again so we have a duplicated encode call.
714             // MYFACES-2726 All '\' and "'" chars must be escaped 
715             // because there will be inside "'" javascript quotes, 
716             // otherwise the value will not correctly restored when
717             // the command is post.
718             //String strParamValue = value != null ? value.toString() : "";
719             String strParamValue = "";
720             if (value != null)
721             {
722                 strParamValue = value.toString();
723                 StringBuilder buff = null;
724                 for (int i = 0; i < strParamValue.length(); i++)
725                 {
726                     char c = strParamValue.charAt(i); 
727                     if (c == '\'' || c == '\\')
728                     {
729                         if (buff == null)
730                         {
731                             buff = new StringBuilder();
732                             buff.append(strParamValue.substring(0,i));
733                         }
734                         buff.append('\\');
735                         buff.append(c);
736                     }
737                     else if (buff != null)
738                     {
739                         buff.append(c);
740                     }
741                 }
742                 if (buff != null)
743                 {
744                     strParamValue = buff.toString();
745                 }
746             }
747 
748             if (params.length() > 1) 
749             {
750                 params.append(",");
751             }
752 
753             params.append("['");
754             params.append(name);
755             params.append("','");
756             params.append(strParamValue);
757             params.append("']");
758         }
759         params.append("]");
760         return params;
761     }
762 
763     /**
764      * find nesting form<br />
765      * need to be overrideable to deal with dummyForm stuff in tomahawk.
766      */
767     protected FormInfo findNestingForm(UIComponent uiComponent, FacesContext facesContext)
768     {
769         return _ComponentUtils.findNestingForm(uiComponent, facesContext);
770     }
771 
772     protected void addHiddenCommandParameter(
773             FacesContext facesContext, UIComponent nestingForm, String hiddenFieldName)
774     {
775         if (nestingForm != null)
776         {
777             HtmlFormRendererBase.addHiddenCommandParameter(facesContext, nestingForm, hiddenFieldName);
778         }
779     }
780 
781 
782     protected void renderNonJavaScriptAnchorStart(FacesContext facesContext,
783                                                   ResponseWriter writer,
784                                                   UIComponent component,
785                                                   String clientId,
786                                                   FormInfo formInfo)
787         throws IOException
788     {
789         ViewHandler viewHandler = facesContext.getApplication().getViewHandler();
790         String viewId = facesContext.getViewRoot().getViewId();
791         String path = viewHandler.getActionURL(facesContext, viewId);
792 
793         boolean strictXhtmlLinks
794                 = MyfacesConfig.getCurrentInstance(facesContext.getExternalContext()).isStrictXhtmlLinks();
795 
796         StringBuilder hrefBuf = new StringBuilder(path);
797 
798         //add clientId parameter for decode
799 
800         if (path.indexOf('?') == -1)
801         {
802             hrefBuf.append('?');
803         }
804         else
805         {
806             if (strictXhtmlLinks)
807             {
808                 hrefBuf.append("&amp;");
809             }
810             else
811             {
812                 hrefBuf.append('&');
813             }
814         }
815         String hiddenFieldName = HtmlRendererUtils.getHiddenCommandLinkFieldName(
816                 formInfo, facesContext);
817         hrefBuf.append(hiddenFieldName);
818         hrefBuf.append('=');
819         hrefBuf.append(clientId);
820 
821         if (getChildCount(component) > 0)
822         {
823             addChildParametersToHref(facesContext, component, hrefBuf,
824                                      false, //not the first url parameter
825                                      writer.getCharacterEncoding());
826         }
827 
828         String href = facesContext.getExternalContext().encodeActionURL(hrefBuf.toString());
829         writer.startElement(HTML.ANCHOR_ELEM, component);
830         writer.writeURIAttribute(HTML.HREF_ATTR,
831                                  facesContext.getExternalContext().encodeActionURL(href),
832                                  null);
833     }
834 
835     private void addChildParametersToHref(FacesContext facesContext,
836                                           UIComponent linkComponent,
837                                           StringBuilder hrefBuf,
838                                           boolean firstParameter,
839                                           String charEncoding)
840             throws IOException
841     {
842         boolean strictXhtmlLinks
843                 = MyfacesConfig.getCurrentInstance(facesContext.getExternalContext()).isStrictXhtmlLinks();
844         List<UIComponent> childrenList = null;
845         if (getChildCount(linkComponent) > 0)
846         {
847             childrenList = getChildren(linkComponent);
848         }
849         else
850         {
851            childrenList = Collections.emptyList();
852         }
853         List<UIParameter> validParams = HtmlRendererUtils.getValidUIParameterChildren(
854                 facesContext, childrenList, false, false);
855         
856         for (int i = 0, size = validParams.size(); i < size; i++)
857         {
858             UIParameter param = validParams.get(i);
859             String name = param.getName();
860             Object value = param.getValue();
861             addParameterToHref(name, value, hrefBuf, firstParameter, charEncoding, strictXhtmlLinks);
862             firstParameter = false;
863         }
864     }
865 
866     protected void renderOutputLinkStart(FacesContext facesContext, UIOutput output)
867             throws IOException
868     {
869         ResponseWriter writer = facesContext.getResponseWriter();
870         Map<String, List<ClientBehavior>> behaviors = null;
871 
872         if (HtmlRendererUtils.isDisabled(output))
873         {
874             writer.startElement(HTML.SPAN_ELEM, output);
875             if (output instanceof ClientBehaviorHolder && JavascriptUtils.isJavascriptAllowed(
876                     facesContext.getExternalContext()))
877             {
878                 behaviors = ((ClientBehaviorHolder) output).getClientBehaviors();
879                 if (!behaviors.isEmpty())
880                 {
881                     HtmlRendererUtils.writeIdAndName(writer, output, facesContext);
882                 }
883                 else
884                 {
885                     HtmlRendererUtils.writeIdIfNecessary(writer, output, facesContext);
886                 }
887                 long commonPropertiesMarked = 0L;
888                 if (isCommonPropertiesOptimizationEnabled(facesContext))
889                 {
890                     commonPropertiesMarked = CommonPropertyUtils.getCommonPropertiesMarked(output);
891                 }
892 
893                 if (behaviors.isEmpty() && isCommonPropertiesOptimizationEnabled(facesContext))
894                 {
895                     CommonPropertyUtils.renderEventProperties(writer, 
896                             commonPropertiesMarked, output);
897                     CommonPropertyUtils.renderFocusBlurEventProperties(writer,
898                             commonPropertiesMarked, output);
899                 }
900                 else
901                 {
902                     if (isCommonEventsOptimizationEnabled(facesContext))
903                     {
904                         Long commonEventsMarked = CommonEventUtils.getCommonEventsMarked(output);
905                         CommonEventUtils.renderBehaviorizedEventHandlers(facesContext, writer, 
906                                 commonPropertiesMarked, commonEventsMarked, output, behaviors);
907                         CommonEventUtils.renderBehaviorizedFieldEventHandlersWithoutOnchangeAndOnselect(
908                             facesContext, writer, commonPropertiesMarked, commonEventsMarked, output, behaviors);
909                     }
910                     else
911                     {
912                         HtmlRendererUtils.renderBehaviorizedEventHandlers(facesContext, writer, output, behaviors);
913                         HtmlRendererUtils.renderBehaviorizedFieldEventHandlersWithoutOnchangeAndOnselect(
914                                 facesContext, writer, output, behaviors);
915                     }
916                 }
917                 if (isCommonPropertiesOptimizationEnabled(facesContext))
918                 {
919                     CommonPropertyUtils.renderAnchorPassthroughPropertiesWithoutEvents(writer, 
920                             commonPropertiesMarked, output);
921                 }
922                 else
923                 {
924                     HtmlRendererUtils.renderHTMLAttributes(writer, output, 
925                             HTML.ANCHOR_PASSTHROUGH_ATTRIBUTES_WITHOUT_EVENTS);
926                 }
927             }
928             else
929             {
930                 HtmlRendererUtils.writeIdIfNecessary(writer, output, facesContext);
931                 if (isCommonPropertiesOptimizationEnabled(facesContext))
932                 {
933                     CommonPropertyUtils.renderAnchorPassthroughProperties(writer, 
934                             CommonPropertyUtils.getCommonPropertiesMarked(output), output);
935                 }
936                 else
937                 {
938                     HtmlRendererUtils.renderHTMLAttributes(writer, output, HTML.ANCHOR_PASSTHROUGH_ATTRIBUTES);
939                 }
940             }
941         }
942         else
943         { 
944             //calculate href
945             String href = org.apache.myfaces.shared.renderkit.RendererUtils.getStringValue(facesContext, output);
946             
947             //check if there is an anchor # in it
948             int index = href.indexOf('#');
949             String anchorString = null;
950             boolean isAnchorInHref = (index > -1);
951             if (isAnchorInHref)
952             {
953                 // remove anchor element and add it again after the parameter are encoded
954                 anchorString = href.substring(index,href.length());
955                 href = href.substring(0,index);
956             }
957             if (getChildCount(output) > 0)
958             {
959                 StringBuilder hrefBuf = new StringBuilder(href);
960                 addChildParametersToHref(facesContext, output, hrefBuf,
961                                      (href.indexOf('?') == -1), //first url parameter?
962                                      writer.getCharacterEncoding());
963                 href = hrefBuf.toString();
964             }
965             // check for the fragement attribute
966             String fragmentAttr = null;
967             if (output instanceof HtmlOutputLink)
968             {
969                 fragmentAttr = ((HtmlOutputLink) output).getFragment();
970             }
971             else
972             {
973                 fragmentAttr = (String) output.getAttributes().get(JSFAttr.FRAGMENT_ATTR);
974             }
975             if (fragmentAttr != null && !"".equals(fragmentAttr)) 
976             {
977                 href += "#" + fragmentAttr;
978             }
979             else if (isAnchorInHref)
980             {
981                 href += anchorString;
982             }
983             href = facesContext.getExternalContext().encodeResourceURL(href);    //TODO: or encodeActionURL ?
984 
985             //write anchor
986             writer.startElement(HTML.ANCHOR_ELEM, output);
987             writer.writeURIAttribute(HTML.HREF_ATTR, href, null);
988             if (output instanceof ClientBehaviorHolder && JavascriptUtils.isJavascriptAllowed(
989                     facesContext.getExternalContext()))
990             {
991                 behaviors = ((ClientBehaviorHolder) output).getClientBehaviors();
992                 if (!behaviors.isEmpty())
993                 {
994                     HtmlRendererUtils.writeIdAndName(writer, output, facesContext);
995                 }
996                 else
997                 {
998                     HtmlRendererUtils.writeIdAndNameIfNecessary(writer, output, facesContext);
999                 }
1000                 long commonPropertiesMarked = 0L;
1001                 if (isCommonPropertiesOptimizationEnabled(facesContext))
1002                 {
1003                     commonPropertiesMarked = CommonPropertyUtils.getCommonPropertiesMarked(output);
1004                 }
1005                 if (behaviors.isEmpty() && isCommonPropertiesOptimizationEnabled(facesContext))
1006                 {
1007                     CommonPropertyUtils.renderEventProperties(writer, 
1008                             commonPropertiesMarked, output);
1009                     CommonPropertyUtils.renderFocusBlurEventProperties(writer,
1010                             commonPropertiesMarked, output);
1011                 }
1012                 else
1013                 {
1014                     if (isCommonEventsOptimizationEnabled(facesContext))
1015                     {
1016                         Long commonEventsMarked = CommonEventUtils.getCommonEventsMarked(output);
1017                         CommonEventUtils.renderBehaviorizedEventHandlers(facesContext, writer, 
1018                                 commonPropertiesMarked, commonEventsMarked, output, behaviors);
1019                         CommonEventUtils.renderBehaviorizedFieldEventHandlersWithoutOnchangeAndOnselect(
1020                             facesContext, writer, commonPropertiesMarked, commonEventsMarked, output, behaviors);
1021                     }
1022                     else
1023                     {
1024                         HtmlRendererUtils.renderBehaviorizedEventHandlers(facesContext, writer, output, behaviors);
1025                         HtmlRendererUtils.renderBehaviorizedFieldEventHandlersWithoutOnchangeAndOnselect(
1026                                 facesContext, writer, output, behaviors);
1027                     }
1028                 }
1029                 if (isCommonPropertiesOptimizationEnabled(facesContext))
1030                 {
1031                     CommonPropertyUtils.renderAnchorPassthroughPropertiesWithoutEvents(writer, 
1032                             commonPropertiesMarked, output);
1033                 }
1034                 else
1035                 {
1036                     HtmlRendererUtils.renderHTMLAttributes(writer, output, 
1037                             HTML.ANCHOR_PASSTHROUGH_ATTRIBUTES_WITHOUT_EVENTS);
1038                 }
1039             }
1040             else
1041             {
1042                 HtmlRendererUtils.writeIdAndNameIfNecessary(writer, output, facesContext);
1043                 if (isCommonPropertiesOptimizationEnabled(facesContext))
1044                 {
1045                     CommonPropertyUtils.renderAnchorPassthroughProperties(writer, 
1046                             CommonPropertyUtils.getCommonPropertiesMarked(output), output);
1047                 }
1048                 else
1049                 {
1050                     HtmlRendererUtils.renderHTMLAttributes(writer, output, HTML.ANCHOR_PASSTHROUGH_ATTRIBUTES);
1051                 }
1052             }
1053             writer.flush();
1054         }
1055     }
1056     
1057     protected void renderOutcomeLinkStart(FacesContext facesContext, UIOutcomeTarget output)
1058             throws IOException
1059     {
1060         ResponseWriter writer = facesContext.getResponseWriter();
1061         Map<String, List<ClientBehavior>> behaviors = null;
1062         
1063         //calculate href
1064         String targetHref = HtmlRendererUtils.getOutcomeTargetHref(facesContext, output);
1065         
1066         if (HtmlRendererUtils.isDisabled(output) || targetHref == null)
1067         {
1068             //output.getAttributes().put(END_LINK_OUTCOME_AS_SPAN, Boolean.TRUE);
1069             //Note one h:link cannot have a nested h:link as a child, so it is safe
1070             //to just put this flag on FacesContext attribute map
1071             facesContext.getAttributes().put(END_LINK_OUTCOME_AS_SPAN, Boolean.TRUE);
1072             writer.startElement(HTML.SPAN_ELEM, output);
1073             if (output instanceof ClientBehaviorHolder && JavascriptUtils.isJavascriptAllowed(
1074                     facesContext.getExternalContext()))
1075             {
1076                 behaviors = ((ClientBehaviorHolder) output).getClientBehaviors();
1077                 if (!behaviors.isEmpty())
1078                 {
1079                     HtmlRendererUtils.writeIdAndName(writer, output, facesContext);
1080                 }
1081                 else
1082                 {
1083                     HtmlRendererUtils.writeIdIfNecessary(writer, output, facesContext);
1084                 }
1085                 long commonPropertiesMarked = 0L;
1086                 if (isCommonPropertiesOptimizationEnabled(facesContext))
1087                 {
1088                     commonPropertiesMarked = CommonPropertyUtils.getCommonPropertiesMarked(output);
1089                 }
1090                 if (behaviors.isEmpty() && isCommonPropertiesOptimizationEnabled(facesContext))
1091                 {
1092                     CommonPropertyUtils.renderEventProperties(writer, 
1093                             commonPropertiesMarked, output);
1094                     CommonPropertyUtils.renderFocusBlurEventProperties(writer,
1095                             commonPropertiesMarked, output);
1096                 }
1097                 else
1098                 {
1099                     if (isCommonEventsOptimizationEnabled(facesContext))
1100                     {
1101                         Long commonEventsMarked = CommonEventUtils.getCommonEventsMarked(output);
1102                         CommonEventUtils.renderBehaviorizedEventHandlers(facesContext, writer, 
1103                                 commonPropertiesMarked, commonEventsMarked, output, behaviors);
1104                         CommonEventUtils.renderBehaviorizedFieldEventHandlersWithoutOnchangeAndOnselect(
1105                             facesContext, writer, commonPropertiesMarked, commonEventsMarked, output, behaviors);
1106                     }
1107                     else
1108                     {
1109                         HtmlRendererUtils.renderBehaviorizedEventHandlers(facesContext, writer, output, behaviors);
1110                         HtmlRendererUtils.renderBehaviorizedFieldEventHandlersWithoutOnchangeAndOnselect(
1111                                 facesContext, writer, output, behaviors);
1112                     }
1113                 }
1114                 if (isCommonPropertiesOptimizationEnabled(facesContext))
1115                 {
1116                     CommonPropertyUtils.renderAnchorPassthroughPropertiesWithoutEvents(writer, 
1117                             commonPropertiesMarked, output);
1118                 }
1119                 else
1120                 {
1121                     HtmlRendererUtils.renderHTMLAttributes(writer, output, 
1122                             HTML.ANCHOR_PASSTHROUGH_ATTRIBUTES_WITHOUT_EVENTS);
1123                 }
1124             }
1125             else
1126             {
1127                 HtmlRendererUtils.writeIdIfNecessary(writer, output, facesContext);
1128                 if (isCommonPropertiesOptimizationEnabled(facesContext))
1129                 {
1130                     CommonPropertyUtils.renderAnchorPassthroughProperties(writer, 
1131                             CommonPropertyUtils.getCommonPropertiesMarked(output), output);
1132                 }
1133                 else
1134                 {
1135                     HtmlRendererUtils.renderHTMLAttributes(writer, output, HTML.ANCHOR_PASSTHROUGH_ATTRIBUTES);
1136                 }
1137             }
1138 
1139             Object value = output.getValue();
1140 
1141             if(value != null)
1142             {
1143                 writer.writeText(value.toString(), JSFAttr.VALUE_ATTR);
1144             }
1145         }
1146         else
1147         {
1148             //write anchor
1149             writer.startElement(HTML.ANCHOR_ELEM, output);
1150             writer.writeURIAttribute(HTML.HREF_ATTR, targetHref, null);
1151             if (output instanceof ClientBehaviorHolder && JavascriptUtils.isJavascriptAllowed(
1152                     facesContext.getExternalContext()))
1153             {
1154                 behaviors = ((ClientBehaviorHolder) output).getClientBehaviors();
1155                 if (!behaviors.isEmpty())
1156                 {
1157                     HtmlRendererUtils.writeIdAndName(writer, output, facesContext);
1158                 }
1159                 else
1160                 {
1161                     HtmlRendererUtils.writeIdAndNameIfNecessary(writer, output, facesContext);
1162                 }
1163                 long commonPropertiesMarked = 0L;
1164                 if (isCommonPropertiesOptimizationEnabled(facesContext))
1165                 {
1166                     commonPropertiesMarked = CommonPropertyUtils.getCommonPropertiesMarked(output);
1167                 }
1168                 if (behaviors.isEmpty() && isCommonPropertiesOptimizationEnabled(facesContext))
1169                 {
1170                     CommonPropertyUtils.renderEventProperties(writer, 
1171                             commonPropertiesMarked, output);
1172                     CommonPropertyUtils.renderFocusBlurEventProperties(writer,
1173                             commonPropertiesMarked, output);
1174                 }
1175                 else
1176                 {
1177                     if (isCommonEventsOptimizationEnabled(facesContext))
1178                     {
1179                         Long commonEventsMarked = CommonEventUtils.getCommonEventsMarked(output);
1180                         CommonEventUtils.renderBehaviorizedEventHandlers(facesContext, writer, 
1181                                 commonPropertiesMarked, commonEventsMarked, output, behaviors);
1182                         CommonEventUtils.renderBehaviorizedFieldEventHandlersWithoutOnchangeAndOnselect(
1183                             facesContext, writer, commonPropertiesMarked, commonEventsMarked, output, behaviors);
1184                     }
1185                     else
1186                     {
1187                         HtmlRendererUtils.renderBehaviorizedEventHandlers(facesContext, writer, output, behaviors);
1188                         HtmlRendererUtils.renderBehaviorizedFieldEventHandlersWithoutOnchangeAndOnselect(
1189                                 facesContext, writer, output, behaviors);
1190                     }
1191                 }
1192                 if (isCommonPropertiesOptimizationEnabled(facesContext))
1193                 {
1194                     CommonPropertyUtils.renderAnchorPassthroughPropertiesWithoutEvents(writer, 
1195                             commonPropertiesMarked, output);
1196                 }
1197                 else
1198                 {
1199                     HtmlRendererUtils.renderHTMLAttributes(writer, output, 
1200                             HTML.ANCHOR_PASSTHROUGH_ATTRIBUTES_WITHOUT_EVENTS);
1201                 }
1202             }
1203             else
1204             {
1205                 HtmlRendererUtils.writeIdAndNameIfNecessary(writer, output, facesContext);
1206                 if (isCommonPropertiesOptimizationEnabled(facesContext))
1207                 {
1208                     CommonPropertyUtils.renderAnchorPassthroughProperties(writer, 
1209                             CommonPropertyUtils.getCommonPropertiesMarked(output), output);
1210                 }
1211                 else
1212                 {
1213                     HtmlRendererUtils.renderHTMLAttributes(writer, output, 
1214                             HTML.ANCHOR_PASSTHROUGH_ATTRIBUTES);
1215                 }
1216             }
1217 
1218             writer.flush();
1219         }
1220     }
1221     
1222 
1223     private void renderLinkParameter(String name,
1224                                      Object value,
1225                                      StringBuilder onClick,
1226                                      String jsForm,
1227                                      UIComponent nestingForm)
1228     {
1229         if (name == null)
1230         {
1231             throw new IllegalArgumentException("Unnamed parameter value not allowed within command link.");
1232         }
1233         onClick.append(jsForm);
1234         onClick.append(".elements['").append(name).append("']");
1235         //UIParameter is no ValueHolder, so no conversion possible
1236         String strParamValue = value != null ? org.apache.myfaces.shared.renderkit.html.util.HTMLEncoder.encode(
1237                 value.toString(), false, false) : "";
1238         onClick.append(".value='").append(strParamValue).append("';");
1239 
1240         addHiddenCommandParameter(FacesContext.getCurrentInstance(), nestingForm, name);
1241     }
1242 
1243     private static void addParameterToHref(String name,
1244                                            Object value,
1245                                            StringBuilder hrefBuf,
1246                                            boolean firstParameter,
1247                                            String charEncoding,
1248                                            boolean strictXhtmlLinks) throws UnsupportedEncodingException
1249     {
1250         if (name == null)
1251         {
1252             throw new IllegalArgumentException("Unnamed parameter value not allowed within command link.");
1253         }
1254 
1255         if (firstParameter)
1256         {
1257             hrefBuf.append('?');
1258         }
1259         else
1260         {
1261             if (strictXhtmlLinks)
1262             {
1263                 hrefBuf.append("&amp;");
1264             }
1265             else
1266             {
1267                 hrefBuf.append('&');
1268             }
1269         }
1270 
1271         hrefBuf.append(URLEncoder.encode(name, charEncoding));
1272         hrefBuf.append('=');
1273         if (value != null)
1274         {
1275             //UIParameter is no ConvertibleValueHolder, so no conversion possible
1276             hrefBuf.append(URLEncoder.encode(value.toString(), charEncoding));
1277         }
1278     }
1279 
1280     protected void renderOutcomeLinkEnd(FacesContext facesContext, UIComponent component)
1281             throws IOException
1282     {
1283         ResponseWriter writer = facesContext.getResponseWriter();
1284         
1285         if (HtmlRendererUtils.isDisabled(component) || Boolean.TRUE.equals(
1286                 facesContext.getAttributes().get(END_LINK_OUTCOME_AS_SPAN)))
1287         {
1288             writer.endElement(HTML.SPAN_ELEM);
1289             facesContext.getAttributes().put(END_LINK_OUTCOME_AS_SPAN, Boolean.FALSE);
1290         }
1291         else
1292         {
1293             writer.writeText (org.apache.myfaces.shared.renderkit.RendererUtils.getStringValue
1294                  (facesContext, component), null);
1295             writer.endElement(HTML.ANCHOR_ELEM);
1296         }
1297     }
1298     
1299     protected void renderOutputLinkEnd(FacesContext facesContext, UIComponent component)
1300             throws IOException
1301     {
1302         ResponseWriter writer = facesContext.getResponseWriter();
1303 
1304         if (HtmlRendererUtils.isDisabled(component))
1305         {
1306             writer.endElement(HTML.SPAN_ELEM);
1307         }
1308         else
1309         {
1310             // force separate end tag
1311             writer.writeText("", null);
1312             writer.endElement(HTML.ANCHOR_ELEM);
1313         }
1314     }
1315 
1316     protected void renderCommandLinkEnd(FacesContext facesContext, UIComponent component)
1317             throws IOException
1318     {
1319         FormInfo formInfo = findNestingForm(component, facesContext);
1320         
1321         ResponseWriter writer = facesContext.getResponseWriter();
1322         if (HtmlRendererUtils.isDisabled(component) || formInfo == null)
1323         {
1324 
1325             writer.endElement(HTML.SPAN_ELEM);
1326         }
1327         else
1328         {
1329             writer.writeText("", null);
1330             writer.endElement(HTML.ANCHOR_ELEM);
1331         }
1332     }
1333 }