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