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