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.util.ArrayList;
23  import java.util.Arrays;
24  import java.util.Collection;
25  import java.util.Collections;
26  import java.util.HashMap;
27  import java.util.Iterator;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.RandomAccess;
31  import java.util.Set;
32  import java.util.StringTokenizer;
33  import java.util.logging.Level;
34  import java.util.logging.Logger;
35  
36  import javax.faces.FacesException;
37  import javax.faces.application.ConfigurableNavigationHandler;
38  import javax.faces.application.NavigationCase;
39  import javax.faces.application.NavigationHandler;
40  import javax.faces.application.ProjectStage;
41  import javax.faces.application.ViewHandler;
42  import javax.faces.component.EditableValueHolder;
43  import javax.faces.component.UIComponent;
44  import javax.faces.component.UIInput;
45  import javax.faces.component.UINamingContainer;
46  import javax.faces.component.UIOutcomeTarget;
47  import javax.faces.component.UIOutput;
48  import javax.faces.component.UIParameter;
49  import javax.faces.component.UISelectBoolean;
50  import javax.faces.component.UISelectMany;
51  import javax.faces.component.UISelectOne;
52  import javax.faces.component.UIViewRoot;
53  import javax.faces.component.behavior.ClientBehavior;
54  import javax.faces.component.behavior.ClientBehaviorContext;
55  import javax.faces.component.behavior.ClientBehaviorHint;
56  import javax.faces.component.behavior.ClientBehaviorHolder;
57  import javax.faces.component.html.HtmlDataTable;
58  import javax.faces.component.html.HtmlMessages;
59  import javax.faces.component.html.HtmlPanelGrid;
60  import javax.faces.context.ExternalContext;
61  import javax.faces.context.FacesContext;
62  import javax.faces.context.ResponseWriter;
63  import javax.faces.convert.Converter;
64  import javax.faces.model.SelectItem;
65  import javax.faces.model.SelectItemGroup;
66  
67  import org.apache.myfaces.shared.application.NavigationUtils;
68  import org.apache.myfaces.shared.component.DisplayValueOnlyCapable;
69  import org.apache.myfaces.shared.component.EscapeCapable;
70  import org.apache.myfaces.shared.config.MyfacesConfig;
71  import org.apache.myfaces.shared.renderkit.ClientBehaviorEvents;
72  import org.apache.myfaces.shared.renderkit.JSFAttr;
73  import org.apache.myfaces.shared.renderkit.RendererUtils;
74  import org.apache.myfaces.shared.renderkit.html.util.FormInfo;
75  import org.apache.myfaces.shared.renderkit.html.util.HTMLEncoder;
76  import org.apache.myfaces.shared.renderkit.html.util.JavascriptUtils;
77  
78  /**
79   * @author Manfred Geiler (latest modification by $Author: lu4242 $)
80   * @version $Revision: 1554917 $ $Date: 2014-01-02 16:13:39 -0500 (Thu, 02 Jan 2014) $
81   */
82  public final class HtmlRendererUtils
83  {
84      //private static final Log log = LogFactory.getLog(HtmlRendererUtils.class);
85      private static final Logger log = Logger.getLogger(HtmlRendererUtils.class
86              .getName());
87      //private static final String[] EMPTY_STRING_ARRAY = new String[0];
88      private static final String LINE_SEPARATOR = System.getProperty(
89              "line.separator", "\r\n");
90      private static final char TABULATOR = '\t';
91      public static final String HIDDEN_COMMANDLINK_FIELD_NAME = "_idcl";
92      public static final String HIDDEN_COMMANDLINK_FIELD_NAME_MYFACES_OLD = "_link_hidden_";
93      public static final String HIDDEN_COMMANDLINK_FIELD_NAME_TRINIDAD = "source";
94      public static final String CLEAR_HIDDEN_FIELD_FN_NAME = "clearFormHiddenParams";
95      public static final String SUBMIT_FORM_FN_NAME = "oamSubmitForm";
96      public static final String SUBMIT_FORM_FN_NAME_JSF2 = "myfaces.oam.submitForm";
97      public static final String ALLOW_CDATA_SECTION_ON = "org.apache.myfaces.ResponseWriter.CdataSectionOn";
98      public static final String NON_SUBMITTED_VALUE_WARNING 
99              = "There should always be a submitted value for an input if it is rendered,"
100             + " its form is submitted, and it was not originally rendered disabled or read-only."
101             + "  You cannot submit a form after disabling an input element via javascript."
102             + "  Consider setting read-only to true instead"
103             + " or resetting the disabled value back to false prior to form submission.";
104     public static final String STR_EMPTY = "";
105 
106     private HtmlRendererUtils()
107     {
108         // utility class, do not instantiate
109     }
110 
111     /**
112      * Utility to set the submitted value of the provided component from the
113      * data in the current request object.
114      * <p/>
115      * Param component is required to be an EditableValueHolder. On return
116      * from this method, the component's submittedValue property will be
117      * set if the submitted form contained that component.
118      */
119     public static void decodeUIInput(FacesContext facesContext, UIComponent component)
120     {
121         if (!(component instanceof EditableValueHolder))
122         {
123             throw new IllegalArgumentException("Component "
124                     + component.getClientId(facesContext)
125                     + " is not an EditableValueHolder");
126         }
127         Map paramMap = facesContext.getExternalContext()
128                 .getRequestParameterMap();
129         String clientId = component.getClientId(facesContext);
130         if (isDisabledOrReadOnly(component))
131         {
132             return;
133         }
134         if (paramMap.containsKey(clientId))
135         {
136             ((EditableValueHolder) component).setSubmittedValue(paramMap
137                     .get(clientId));
138         }
139         else
140         {
141             log.warning(NON_SUBMITTED_VALUE_WARNING + " Component : "
142                     + RendererUtils.getPathToComponent(component));
143         }
144     }
145 
146     /**
147      * X-CHECKED: tlddoc h:selectBooleanCheckbox
148      *
149      * @param facesContext
150      * @param component
151      */
152     public static void decodeUISelectBoolean(FacesContext facesContext, UIComponent component)
153     {
154         if (!(component instanceof EditableValueHolder))
155         {
156             throw new IllegalArgumentException("Component "
157                     + component.getClientId(facesContext)
158                     + " is not an EditableValueHolder");
159         }
160         if (isDisabledOrReadOnly(component))
161         {
162             return;
163         }
164         Map paramMap = facesContext.getExternalContext()
165                 .getRequestParameterMap();
166         String clientId = component.getClientId(facesContext);
167         if (paramMap.containsKey(clientId))
168         {
169             String reqValue = (String) paramMap.get(clientId);
170             if ((reqValue.equalsIgnoreCase("on")
171                     || reqValue.equalsIgnoreCase("yes") || reqValue
172                     .equalsIgnoreCase("true")))
173             {
174                 ((EditableValueHolder) component).setSubmittedValue(Boolean.TRUE);
175             }
176             else
177             {
178                 ((EditableValueHolder) component).setSubmittedValue(Boolean.FALSE);
179             }
180         }
181         else
182         {
183             ((EditableValueHolder) component).setSubmittedValue(Boolean.FALSE);
184         }
185     }
186 
187     public static boolean isDisabledOrReadOnly(UIComponent component)
188     {
189         return isDisplayValueOnly(component) || isDisabled(component) || isReadOnly(component);
190     }
191 
192     public static boolean isDisabled(UIComponent component)
193     {
194         return isTrue(component.getAttributes().get("disabled"));
195     }
196 
197     public static boolean isReadOnly(UIComponent component)
198     {
199         return isTrue(component.getAttributes().get("readonly"));
200     }
201 
202     private static boolean isTrue(Object obj)
203     {
204         if (obj instanceof String)
205         {
206             return new Boolean((String) obj);
207         }
208         if (!(obj instanceof Boolean))
209         {
210             return false;
211         }
212         return ((Boolean) obj).booleanValue();
213     }
214 
215     /**
216      * X-CHECKED: tlddoc h:selectManyListbox
217      *
218      * @param facesContext
219      * @param component
220      */
221     public static void decodeUISelectMany(FacesContext facesContext, UIComponent component)
222     {
223         if (!(component instanceof EditableValueHolder))
224         {
225             throw new IllegalArgumentException("Component "
226                     + component.getClientId(facesContext)
227                     + " is not an EditableValueHolder");
228         }
229         Map paramValuesMap = facesContext.getExternalContext().getRequestParameterValuesMap();
230         String clientId = component.getClientId(facesContext);
231         if (isDisabledOrReadOnly(component))
232         {
233             return;
234         }
235         if (paramValuesMap.containsKey(clientId))
236         {
237             String[] reqValues = (String[]) paramValuesMap.get(clientId);
238             ((EditableValueHolder) component).setSubmittedValue(reqValues);
239         }
240         else
241         {
242             /* request parameter not found, nothing to decode - set submitted value to an empty array
243                as we should get here only if the component is on a submitted form, is rendered
244                and if the component is not readonly or has not been disabled.
245                So in fact, there must be component value at this location, but for listboxes, comboboxes etc.
246                the submitted value is not posted if no item is selected. */
247             ((EditableValueHolder) component).setSubmittedValue(new String[] {});
248         }
249     }
250 
251     /**
252      * X-CHECKED: tlddoc h:selectManyListbox
253      *
254      * @param facesContext
255      * @param component
256      */
257     public static void decodeUISelectOne(FacesContext facesContext, UIComponent component)
258     {
259         if (!(component instanceof EditableValueHolder))
260         {
261             throw new IllegalArgumentException("Component "
262                     + component.getClientId(facesContext)
263                     + " is not an EditableValueHolder");
264         }
265         if (isDisabledOrReadOnly(component))
266         {
267             return;
268         }
269         Map paramMap = facesContext.getExternalContext().getRequestParameterMap();
270         String clientId = component.getClientId(facesContext);
271         if (paramMap.containsKey(clientId))
272         {
273             //request parameter found, set submitted value
274             ((EditableValueHolder) component).setSubmittedValue(paramMap.get(clientId));
275         }
276         else
277         {
278             //see reason for this action at decodeUISelectMany
279             ((EditableValueHolder) component).setSubmittedValue(STR_EMPTY);
280         }
281     }
282 
283     /**
284      * @since 4.0.0
285      */
286     public static void decodeClientBehaviors(FacesContext facesContext, UIComponent component)
287     {
288         if (component instanceof ClientBehaviorHolder)
289         {
290             ClientBehaviorHolder clientBehaviorHolder = (ClientBehaviorHolder) component;
291             Map<String, List<ClientBehavior>> clientBehaviors = clientBehaviorHolder
292                     .getClientBehaviors();
293             if (clientBehaviors != null && !clientBehaviors.isEmpty())
294             {
295                 Map<String, String> paramMap = facesContext
296                         .getExternalContext().getRequestParameterMap();
297                 String behaviorEventName = paramMap
298                         .get("javax.faces.behavior.event");
299                 if (behaviorEventName != null)
300                 {
301                     List<ClientBehavior> clientBehaviorList = clientBehaviors
302                             .get(behaviorEventName);
303                     if (clientBehaviorList != null
304                             && !clientBehaviorList.isEmpty())
305                     {
306                         String clientId = paramMap.get("javax.faces.source");
307                         if (component.getClientId(facesContext).equals(clientId))
308                         {
309                             if (clientBehaviorList instanceof RandomAccess)
310                             {
311                                 for (int i = 0, size = clientBehaviorList.size(); i < size; i++)
312                                 {
313                                     ClientBehavior clientBehavior = clientBehaviorList.get(i);
314                                     clientBehavior.decode(facesContext, component);
315                                 }
316                             } 
317                             else
318                             {
319                                 for (ClientBehavior clientBehavior : clientBehaviorList)
320                                 {
321                                     clientBehavior.decode(facesContext, component);
322                                 }
323                             }
324                         }
325                     }
326                 }
327             }
328         }
329     }
330 
331     public static void renderListbox(FacesContext facesContext,
332             UISelectOne selectOne, boolean disabled, int size,
333             Converter converter) throws IOException
334     {
335         internalRenderSelect(facesContext, selectOne, disabled, size, false, converter);
336     }
337 
338     public static void renderListbox(FacesContext facesContext,
339             UISelectMany selectMany, boolean disabled, int size,
340             Converter converter) throws IOException
341     {
342         internalRenderSelect(facesContext, selectMany, disabled, size, true, converter);
343     }
344 
345     public static void renderMenu(FacesContext facesContext,
346             UISelectOne selectOne, boolean disabled, Converter converter)
347             throws IOException
348     {
349         internalRenderSelect(facesContext, selectOne, disabled, 1, false, converter);
350     }
351 
352     public static void renderMenu(FacesContext facesContext,
353             UISelectMany selectMany, boolean disabled, Converter converter)
354             throws IOException
355     {
356         internalRenderSelect(facesContext, selectMany, disabled, 1, true, converter);
357     }
358 
359     private static void internalRenderSelect(FacesContext facesContext,
360             UIComponent uiComponent, boolean disabled, int size,
361             boolean selectMany, Converter converter) throws IOException
362     {
363         ResponseWriter writer = facesContext.getResponseWriter();
364         writer.startElement(HTML.SELECT_ELEM, uiComponent);
365         if (uiComponent instanceof ClientBehaviorHolder
366                 && JavascriptUtils.isJavascriptAllowed(facesContext.getExternalContext())
367                 && !((ClientBehaviorHolder) uiComponent).getClientBehaviors().isEmpty())
368         {
369             writer.writeAttribute(HTML.ID_ATTR, uiComponent.getClientId(facesContext), null);
370         }
371         else
372         {
373             HtmlRendererUtils.writeIdIfNecessary(writer, uiComponent, facesContext);
374         }
375         writer.writeAttribute(HTML.NAME_ATTR,
376                 uiComponent.getClientId(facesContext), null);
377         List selectItemList;
378         if (selectMany)
379         {
380             writer.writeAttribute(HTML.MULTIPLE_ATTR, HTML.MULTIPLE_ATTR, null);
381             selectItemList = org.apache.myfaces.shared.renderkit.RendererUtils
382                     .getSelectItemList((UISelectMany) uiComponent, facesContext);
383         }
384         else
385         {
386             selectItemList = RendererUtils.getSelectItemList(
387                     (UISelectOne) uiComponent, facesContext);
388         }
389 
390         if (size == Integer.MIN_VALUE)
391         {
392             //No size given (Listbox) --> size is number of select items
393             writer.writeAttribute(HTML.SIZE_ATTR,
394                     Integer.toString(selectItemList.size()), null);
395         }
396         else
397         {
398             writer.writeAttribute(HTML.SIZE_ATTR, Integer.toString(size), null);
399         }
400         Map<String, List<ClientBehavior>> behaviors = null;
401         if (uiComponent instanceof ClientBehaviorHolder
402                 && JavascriptUtils.isJavascriptAllowed(facesContext
403                         .getExternalContext()))
404         {
405             behaviors = ((ClientBehaviorHolder) uiComponent)
406                     .getClientBehaviors();
407             renderBehaviorizedOnchangeEventHandler(facesContext, writer, uiComponent, behaviors);
408             renderBehaviorizedEventHandlers(facesContext, writer, uiComponent, behaviors);
409             renderBehaviorizedFieldEventHandlersWithoutOnchange(facesContext, writer, uiComponent, behaviors);
410             renderHTMLAttributes(
411                     writer,
412                     uiComponent,
413                     HTML.SELECT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED_AND_EVENTS);
414         }
415         else
416         {
417             renderHTMLAttributes(writer, uiComponent,
418                     HTML.SELECT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED);
419         }
420 
421         if (disabled)
422         {
423             writer.writeAttribute(HTML.DISABLED_ATTR, Boolean.TRUE, null);
424         }
425 
426         if (isReadOnly(uiComponent))
427         {
428             writer.writeAttribute(HTML.READONLY_ATTR, HTML.READONLY_ATTR, null);
429         }
430 
431         Set lookupSet = getSubmittedOrSelectedValuesAsSet(selectMany,
432                 uiComponent, facesContext, converter);
433 
434         renderSelectOptions(facesContext, uiComponent, converter, lookupSet,
435                 selectItemList);
436         // bug #970747: force separate end tag
437         writer.writeText(STR_EMPTY, null);
438         writer.endElement(HTML.SELECT_ELEM);
439     }
440 
441     public static Set getSubmittedOrSelectedValuesAsSet(boolean selectMany,
442             UIComponent uiComponent, FacesContext facesContext, Converter converter)
443     {
444         Set lookupSet;
445         if (selectMany)
446         {
447             UISelectMany uiSelectMany = (UISelectMany) uiComponent;
448             lookupSet = RendererUtils.getSubmittedValuesAsSet(facesContext,
449                     uiComponent, converter, uiSelectMany);
450             if (lookupSet == null)
451             {
452                 lookupSet = RendererUtils.getSelectedValuesAsSet(facesContext,
453                         uiComponent, converter, uiSelectMany);
454             }
455         }
456         else
457         {
458             UISelectOne uiSelectOne = (UISelectOne) uiComponent;
459             Object lookup = uiSelectOne.getSubmittedValue();
460             if (lookup == null)
461             {
462                 lookup = uiSelectOne.getValue();
463                 String lookupString = RendererUtils.getConvertedStringValue(
464                         facesContext, uiComponent, converter, lookup);
465                 lookupSet = Collections.singleton(lookupString);
466             }
467             else if (STR_EMPTY.equals(lookup))
468             {
469                 lookupSet = Collections.EMPTY_SET;
470             }
471             else
472             {
473                 lookupSet = Collections.singleton(lookup);
474             }
475         }
476         return lookupSet;
477     }
478 
479     public static Converter findUISelectManyConverterFailsafe(
480             FacesContext facesContext, UIComponent uiComponent)
481     {
482         // invoke with considerValueType = false
483         return findUISelectManyConverterFailsafe(facesContext, uiComponent, false);
484     }
485 
486     public static Converter findUISelectManyConverterFailsafe(
487             FacesContext facesContext, UIComponent uiComponent,
488             boolean considerValueType)
489     {
490         Converter converter;
491         try
492         {
493             converter = RendererUtils.findUISelectManyConverter(facesContext,
494                     (UISelectMany) uiComponent, considerValueType);
495         }
496         catch (FacesException e)
497         {
498             log.log(Level.SEVERE,
499                     "Error finding Converter for component with id "
500                             + uiComponent.getClientId(facesContext), e);
501             converter = null;
502         }
503         return converter;
504     }
505 
506     public static Converter findUIOutputConverterFailSafe(FacesContext facesContext, UIComponent uiComponent)
507     {
508         Converter converter;
509         try
510         {
511             converter = RendererUtils.findUIOutputConverter(facesContext, (UIOutput) uiComponent);
512         }
513         catch (FacesException e)
514         {
515             log.log(Level.SEVERE,
516                     "Error finding Converter for component with id "
517                             + uiComponent.getClientId(facesContext), e);
518             converter = null;
519         }
520         return converter;
521     }
522 
523     /**
524      * Renders the select options for a <code>UIComponent</code> that is
525      * rendered as an HTML select element.
526      *
527      * @param context        the current <code>FacesContext</code>.
528      * @param component      the <code>UIComponent</code> whose options need to be
529      *                       rendered.
530      * @param converter      <code>component</code>'s converter
531      * @param lookupSet      the <code>Set</code> to use to look up selected options
532      * @param selectItemList the <code>List</code> of <code>SelectItem</code> s to be
533      *                       rendered as HTML option elements.
534      * @throws IOException
535      */
536     public static void renderSelectOptions(FacesContext context,
537             UIComponent component, Converter converter, Set lookupSet,
538             List selectItemList) throws IOException
539     {
540         ResponseWriter writer = context.getResponseWriter();
541         // check for the hideNoSelectionOption attribute
542         boolean hideNoSelectionOption = isHideNoSelectionOption(component);
543         boolean componentDisabled = isTrue(component.getAttributes()
544                 .get("disabled"));
545 
546         for (Iterator it = selectItemList.iterator(); it.hasNext();)
547         {
548             SelectItem selectItem = (SelectItem) it.next();
549             if (selectItem instanceof SelectItemGroup)
550             {
551                 writer.startElement(HTML.OPTGROUP_ELEM, component);
552                 writer.writeAttribute(HTML.LABEL_ATTR, selectItem.getLabel(),
553                         null);
554                 SelectItem[] selectItems = ((SelectItemGroup) selectItem)
555                         .getSelectItems();
556                 renderSelectOptions(context, component, converter, lookupSet,
557                         Arrays.asList(selectItems));
558                 writer.endElement(HTML.OPTGROUP_ELEM);
559             }
560             else
561             {
562                 String itemStrValue = org.apache.myfaces.shared.renderkit.RendererUtils
563                         .getConvertedStringValue(context, component, converter,
564                                 selectItem);
565                 boolean selected = lookupSet.contains(itemStrValue); 
566                 //TODO/FIX: we always compare the String vales, better fill lookupSet with Strings 
567                 //only when useSubmittedValue==true, else use the real item value Objects
568 
569                 // IF the hideNoSelectionOption attribute of the component is true
570                 // AND this selectItem is the "no selection option"
571                 // AND there are currently selected items 
572                 // AND this item (the "no selection option") is not selected
573                 // (if there is currently no value on UISelectOne, lookupSet contains "")
574                 if (hideNoSelectionOption && selectItem.isNoSelectionOption()
575                         && lookupSet.size() != 0
576                         && !(lookupSet.size() == 1 && lookupSet.contains(""))
577                         && !selected)
578                 {
579                     // do not render this selectItem
580                     continue;
581                 }
582 
583                 writer.write(TABULATOR);
584                 writer.startElement(HTML.OPTION_ELEM, component);
585                 if (itemStrValue != null)
586                 {
587                     writer.writeAttribute(HTML.VALUE_ATTR, itemStrValue, null);
588                 }
589                 else
590                 {
591                     writer.writeAttribute(HTML.VALUE_ATTR, "", null);
592                 }
593 
594                 if (selected)
595                 {
596                     writer.writeAttribute(HTML.SELECTED_ATTR, HTML.SELECTED_ATTR, null);
597                 }
598 
599                 boolean disabled = selectItem.isDisabled();
600                 if (disabled)
601                 {
602                     writer.writeAttribute(HTML.DISABLED_ATTR, HTML.DISABLED_ATTR, null);
603                 }
604 
605                 String labelClass = null;
606 
607                 if (componentDisabled || disabled)
608                 {
609                     labelClass = (String) component.getAttributes().get(
610                             JSFAttr.DISABLED_CLASS_ATTR);
611                 }
612                 else
613                 {
614                     labelClass = (String) component.getAttributes().get(
615                             JSFAttr.ENABLED_CLASS_ATTR);
616                 }
617                 if (labelClass != null)
618                 {
619                     writer.writeAttribute("class", labelClass, "labelClass");
620                 }
621 
622                 boolean escape;
623                 if (component instanceof EscapeCapable)
624                 {
625                     escape = ((EscapeCapable) component).isEscape();
626 
627                     // Preserve tomahawk semantic. If escape=false
628                     // all items should be non escaped. If escape
629                     // is true check if selectItem.isEscape() is
630                     // true and do it.
631                     // This is done for remain compatibility.
632                     if (escape && selectItem.isEscape())
633                     {
634                         writer.writeText(selectItem.getLabel(), null);
635                     }
636                     else
637                     {
638                         writer.write(selectItem.getLabel());
639                     }
640                 }
641                 else
642                 {
643                     escape = RendererUtils.getBooleanAttribute(component,
644                             JSFAttr.ESCAPE_ATTR, false);
645                     //default is to escape
646                     //In JSF 1.2, when a SelectItem is created by default 
647                     //selectItem.isEscape() returns true (this property
648                     //is not available on JSF 1.1).
649                     //so, if we found a escape property on the component
650                     //set to true, escape every item, but if not
651                     //check if isEscape() = true first.
652                     if (escape || selectItem.isEscape())
653                     {
654                         writer.writeText(selectItem.getLabel(), null);
655                     }
656                     else
657                     {
658                         writer.write(selectItem.getLabel());
659                     }
660                 }
661 
662                 writer.endElement(HTML.OPTION_ELEM);
663             }
664         }
665     }
666 
667     public static void writePrettyLineSeparator(FacesContext facesContext)
668             throws IOException
669     {
670         if (org.apache.myfaces.shared.config.MyfacesConfig.getCurrentInstance(
671                 facesContext.getExternalContext()).isPrettyHtml())
672         {
673             facesContext.getResponseWriter().write(LINE_SEPARATOR);
674         }
675     }
676 
677     public static void writePrettyIndent(FacesContext facesContext)
678             throws IOException
679     {
680         if (org.apache.myfaces.shared.config.MyfacesConfig.getCurrentInstance(
681                 facesContext.getExternalContext()).isPrettyHtml())
682         {
683             facesContext.getResponseWriter().write('\t');
684         }
685     }
686 
687     /**
688      * @return true, if the attribute was written
689      * @throws java.io.IOException
690      */
691     public static boolean renderHTMLAttribute(ResponseWriter writer,
692             String componentProperty, String attrName, Object value)
693             throws IOException
694     {
695         if (!RendererUtils.isDefaultAttributeValue(value))
696         {
697             // render JSF "styleClass" and "itemStyleClass" attributes as "class"
698             String htmlAttrName = attrName.equals(HTML.STYLE_CLASS_ATTR) ? HTML.CLASS_ATTR
699                     : attrName;
700             writer.writeAttribute(htmlAttrName, value, componentProperty);
701             return true;
702         }
703 
704         return false;
705     }
706 
707     /**
708      * @return true, if the attribute was written
709      * @throws java.io.IOException
710      */
711     public static boolean renderHTMLAttribute(ResponseWriter writer,
712             UIComponent component, String componentProperty, String htmlAttrName)
713             throws IOException
714     {
715         Object value = component.getAttributes().get(componentProperty);
716         return renderHTMLAttribute(writer, componentProperty, htmlAttrName,
717                 value);
718     }
719 
720     /**
721      * @return true, if an attribute was written
722      * @throws java.io.IOException
723      */
724     public static boolean renderHTMLAttributes(ResponseWriter writer,
725             UIComponent component, String[] attributes) throws IOException
726     {
727         boolean somethingDone = false;
728         for (int i = 0, len = attributes.length; i < len; i++)
729         {
730             String attrName = attributes[i];
731             if (renderHTMLAttribute(writer, component, attrName, attrName))
732             {
733                 somethingDone = true;
734             }
735         }
736         return somethingDone;
737     }
738 
739     public static boolean renderHTMLAttributeWithOptionalStartElement(
740             ResponseWriter writer, UIComponent component, String elementName,
741             String attrName, Object value, boolean startElementWritten)
742             throws IOException
743     {
744         if (!org.apache.myfaces.shared.renderkit.RendererUtils
745                 .isDefaultAttributeValue(value))
746         {
747             if (!startElementWritten)
748             {
749                 writer.startElement(elementName, component);
750                 startElementWritten = true;
751             }
752             renderHTMLAttribute(writer, attrName, attrName, value);
753         }
754         return startElementWritten;
755     }
756 
757     public static boolean renderHTMLAttributesWithOptionalStartElement(
758             ResponseWriter writer, UIComponent component, String elementName,
759             String[] attributes) throws IOException
760     {
761         boolean startElementWritten = false;
762         for (int i = 0, len = attributes.length; i < len; i++)
763         {
764             String attrName = attributes[i];
765             Object value = component.getAttributes().get(attrName);
766             if (!RendererUtils.isDefaultAttributeValue(value))
767             {
768                 if (!startElementWritten)
769                 {
770                     writer.startElement(elementName, component);
771                     startElementWritten = true;
772                 }
773                 renderHTMLAttribute(writer, attrName, attrName, value);
774             }
775         }
776         return startElementWritten;
777     }
778 
779     public static boolean renderOptionalEndElement(ResponseWriter writer,
780             UIComponent component, String elementName, String[] attributes)
781             throws IOException
782     {
783         boolean endElementNeeded = false;
784         for (int i = 0, len = attributes.length; i < len; i++)
785         {
786             String attrName = attributes[i];
787             Object value = component.getAttributes().get(attrName);
788             if (!RendererUtils.isDefaultAttributeValue(value))
789             {
790                 endElementNeeded = true;
791                 break;
792             }
793         }
794         if (endElementNeeded)
795         {
796             writer.endElement(elementName);
797             return true;
798         }
799 
800         return false;
801     }
802 
803     public static void writeIdIfNecessary(ResponseWriter writer,
804             UIComponent component, FacesContext facesContext)
805             throws IOException
806     {
807         if (component.getId() != null
808                 && !component.getId().startsWith(UIViewRoot.UNIQUE_ID_PREFIX))
809         {
810             writer.writeAttribute(HTML.ID_ATTR, component.getClientId(facesContext), null);
811         }
812     }
813 
814     public static void writeIdAndNameIfNecessary(ResponseWriter writer,
815             UIComponent component, FacesContext facesContext)
816             throws IOException
817     {
818         if (component.getId() != null
819                 && !component.getId().startsWith(UIViewRoot.UNIQUE_ID_PREFIX))
820         {
821             String clientId = component.getClientId(facesContext);
822             writer.writeAttribute(HTML.ID_ATTR, clientId, null);
823             writer.writeAttribute(HTML.NAME_ATTR, clientId, null);
824         }
825     }
826     
827     /**
828      * Renders a html string type attribute. If the value retrieved from the component 
829      * property is "", the attribute is rendered.
830      * 
831      * @param writer
832      * @param component
833      * @param componentProperty
834      * @param htmlAttrName
835      * @return
836      * @throws IOException
837      */
838     public static final boolean renderHTMLStringPreserveEmptyAttribute(ResponseWriter writer,
839             UIComponent component, String componentProperty, String htmlAttrName)
840             throws IOException
841     {
842         String value = (String) component.getAttributes().get(componentProperty);
843         if (!isDefaultStringPreserveEmptyAttributeValue(value))
844         {
845             writer.writeAttribute(htmlAttrName, value, componentProperty);
846             return true;
847         }
848         return false;
849     }
850     
851     /**
852      * Renders a html string type attribute. If the value retrieved from the component 
853      * property is "", the attribute is rendered.
854      * 
855      * @param writer
856      * @param component
857      * @param componentProperty
858      * @param htmlAttrName
859      * @return
860      * @throws IOException
861      */
862     public static final boolean renderHTMLStringPreserveEmptyAttribute(ResponseWriter writer,
863             String componentProperty, String htmlAttrName, String value)
864             throws IOException
865     {
866         if (!isDefaultStringPreserveEmptyAttributeValue(value))
867         {
868             writer.writeAttribute(htmlAttrName, value, componentProperty);
869             return true;
870         }
871         return false;
872     }
873 
874     /**
875      * Check if the value is the default for String type attributes that requires preserve "" as
876      * a valid value.
877      * 
878      * @param value
879      * @return
880      */
881     private static final boolean isDefaultStringPreserveEmptyAttributeValue(String value)
882     {
883         if (value == null)
884         {
885             return true;
886         }
887         else
888         {
889             return false;
890         }
891     }
892 
893     /**
894      * Renders a html string type attribute. If the value retrieved from the component 
895      * property is "" or null, the attribute is not rendered.
896      * 
897      * @param writer
898      * @param component
899      * @param componentProperty
900      * @param htmlAttrName
901      * @return
902      * @throws IOException
903      */
904     public static final boolean renderHTMLStringAttribute(ResponseWriter writer,
905             UIComponent component, String componentProperty, String htmlAttrName)
906             throws IOException
907     {
908         String value = (String) component.getAttributes().get(componentProperty);
909         if (!isDefaultStringAttributeValue(value))
910         {
911             writer.writeAttribute(htmlAttrName, value, componentProperty);
912             return true;
913         }
914         return false;
915     }
916 
917     /**
918      * Renders a html string type attribute. If the value retrieved from the component 
919      * property is "" or null, the attribute is not rendered.
920      * 
921      * @param writer
922      * @param componentProperty
923      * @param htmlAttrName
924      * @param value
925      * @return
926      * @throws IOException
927      */
928     public static final boolean renderHTMLStringAttribute(ResponseWriter writer,
929             String componentProperty, String htmlAttrName, String value)
930             throws IOException
931     {
932         if (!isDefaultStringAttributeValue(value))
933         {
934             writer.writeAttribute(htmlAttrName, value, componentProperty);
935             return true;
936         }
937         return false;
938     }
939     
940     /**
941      * Check if the value is the default for String type attributes (null or "").
942      * 
943      * @param value
944      * @return
945      */
946     private static final boolean isDefaultStringAttributeValue(String value)
947     {
948         if (value == null)
949         {
950             return true;
951         }
952         else if (value.length() == 0)
953         {
954             return true;
955         }
956         else
957         {
958             return false;
959         }
960     }
961     
962     public static boolean renderHTMLStringNoStyleAttributes(ResponseWriter writer,
963             UIComponent component, String[] attributes) throws IOException
964     {
965         boolean somethingDone = false;
966         for (int i = 0, len = attributes.length; i < len; i++)
967         {
968             String attrName = attributes[i];
969             if (renderHTMLStringAttribute(writer, component, attrName, attrName))
970             {
971                 somethingDone = true;
972             }
973         }
974         return somethingDone;
975     }
976 
977     public static void writeIdAndName(ResponseWriter writer, UIComponent component, FacesContext facesContext)
978             throws IOException
979     {
980         String clientId = component.getClientId(facesContext);
981         writer.writeAttribute(HTML.ID_ATTR, clientId, null);
982         writer.writeAttribute(HTML.NAME_ATTR, clientId, null);
983     }
984 
985     public static void renderDisplayValueOnlyForSelects(
986             FacesContext facesContext, UIComponent uiComponent)
987             throws IOException
988     {
989         // invoke renderDisplayValueOnlyForSelects with considerValueType = false
990         renderDisplayValueOnlyForSelects(facesContext, uiComponent, false);
991     }
992 
993     public static void renderDisplayValueOnlyForSelects(FacesContext facesContext, UIComponent uiComponent,
994             boolean considerValueType) throws IOException
995     {
996         ResponseWriter writer = facesContext.getResponseWriter();
997 
998         List selectItemList = null;
999         Converter converter = null;
1000         boolean isSelectOne = false;
1001 
1002         if (uiComponent instanceof UISelectBoolean)
1003         {
1004             converter = findUIOutputConverterFailSafe(facesContext, uiComponent);
1005 
1006             writer.startElement(HTML.SPAN_ELEM, uiComponent);
1007             writeIdIfNecessary(writer, uiComponent, facesContext);
1008             renderDisplayValueOnlyAttributes(uiComponent, writer);
1009             writer.writeText(RendererUtils.getConvertedStringValue(
1010                     facesContext, uiComponent, converter,
1011                     ((UISelectBoolean) uiComponent).getValue()),
1012                     JSFAttr.VALUE_ATTR);
1013             writer.endElement(HTML.SPAN_ELEM);
1014 
1015         }
1016         else
1017         {
1018             if (uiComponent instanceof UISelectMany)
1019             {
1020                 isSelectOne = false;
1021                 selectItemList = RendererUtils.getSelectItemList(
1022                         (UISelectMany) uiComponent, facesContext);
1023                 converter = findUISelectManyConverterFailsafe(facesContext,
1024                         uiComponent, considerValueType);
1025             }
1026             else if (uiComponent instanceof UISelectOne)
1027             {
1028                 isSelectOne = true;
1029                 selectItemList = RendererUtils.getSelectItemList(
1030                         (UISelectOne) uiComponent, facesContext);
1031                 converter = findUIOutputConverterFailSafe(facesContext,
1032                         uiComponent);
1033             }
1034 
1035             writer.startElement(isSelectOne ? HTML.SPAN_ELEM : HTML.UL_ELEM, uiComponent);
1036             writeIdIfNecessary(writer, uiComponent, facesContext);
1037 
1038             renderDisplayValueOnlyAttributes(uiComponent, writer);
1039 
1040             Set lookupSet = getSubmittedOrSelectedValuesAsSet(
1041                     uiComponent instanceof UISelectMany, uiComponent,
1042                     facesContext, converter);
1043 
1044             renderSelectOptionsAsText(facesContext, uiComponent, converter,
1045                     lookupSet, selectItemList, isSelectOne);
1046 
1047             // bug #970747: force separate end tag
1048             writer.writeText(STR_EMPTY, null);
1049             writer.endElement(isSelectOne ? HTML.SPAN_ELEM : HTML.UL_ELEM);
1050         }
1051 
1052     }
1053 
1054     public static void renderDisplayValueOnlyAttributes(
1055             UIComponent uiComponent, ResponseWriter writer) throws IOException
1056     {
1057         if (!(uiComponent instanceof org.apache.myfaces.shared.component.DisplayValueOnlyCapable))
1058         {
1059             log.severe("Wrong type of uiComponent. needs DisplayValueOnlyCapable.");
1060             renderHTMLAttributes(writer, uiComponent, HTML.COMMON_PASSTROUGH_ATTRIBUTES);
1061 
1062             return;
1063         }
1064 
1065         if (getDisplayValueOnlyStyle(uiComponent) != null
1066                 || getDisplayValueOnlyStyleClass(uiComponent) != null)
1067         {
1068             if (getDisplayValueOnlyStyle(uiComponent) != null)
1069             {
1070                 writer.writeAttribute(HTML.STYLE_ATTR, getDisplayValueOnlyStyle(uiComponent), null);
1071             }
1072             else if (uiComponent.getAttributes().get("style") != null)
1073             {
1074                 writer.writeAttribute(HTML.STYLE_ATTR, uiComponent.getAttributes().get("style"), null);
1075             }
1076 
1077             if (getDisplayValueOnlyStyleClass(uiComponent) != null)
1078             {
1079                 writer.writeAttribute(HTML.CLASS_ATTR, getDisplayValueOnlyStyleClass(uiComponent), null);
1080             }
1081             else if (uiComponent.getAttributes().get("styleClass") != null)
1082             {
1083                 writer.writeAttribute(HTML.CLASS_ATTR, uiComponent.getAttributes().get("styleClass"), null);
1084             }
1085 
1086             renderHTMLAttributes(writer, uiComponent, HTML.COMMON_PASSTROUGH_ATTRIBUTES_WITHOUT_STYLE);
1087         }
1088         else
1089         {
1090             renderHTMLAttributes(writer, uiComponent, HTML.COMMON_PASSTROUGH_ATTRIBUTES);
1091         }
1092     }
1093 
1094     private static void renderSelectOptionsAsText(FacesContext context,
1095             UIComponent component, Converter converter, Set lookupSet,
1096             List selectItemList, boolean isSelectOne) throws IOException
1097     {
1098         ResponseWriter writer = context.getResponseWriter();
1099 
1100         for (Iterator it = selectItemList.iterator(); it.hasNext();)
1101         {
1102             SelectItem selectItem = (SelectItem) it.next();
1103 
1104             if (selectItem instanceof SelectItemGroup)
1105             {
1106                 SelectItem[] selectItems = ((SelectItemGroup) selectItem).getSelectItems();
1107                 renderSelectOptionsAsText(context, component, converter,
1108                         lookupSet, Arrays.asList(selectItems), isSelectOne);
1109             }
1110             else
1111             {
1112                 String itemStrValue = RendererUtils.getConvertedStringValue(
1113                         context, component, converter, selectItem);
1114 
1115                 if (lookupSet.contains(itemStrValue))
1116                 {
1117                     //TODO/FIX: we always compare the String vales, better fill lookupSet with Strings 
1118                     //only when useSubmittedValue==true, else use the real item value Objects
1119                     if (!isSelectOne)
1120                     {
1121                         writer.startElement(HTML.LI_ELEM, component);
1122                     }
1123                     writer.writeText(selectItem.getLabel(), null);
1124                     if (!isSelectOne)
1125                     {
1126                         writer.endElement(HTML.LI_ELEM);
1127                     }
1128                     if (isSelectOne)
1129                     {
1130                         //take care of several choices with the same value; use only the first one
1131                         return;
1132                     }
1133                 }
1134             }
1135         }
1136     }
1137 
1138     public static void renderTableCaption(FacesContext context,
1139             ResponseWriter writer, UIComponent component) throws IOException
1140     {
1141         UIComponent captionFacet = component.getFacet("caption");
1142         if (captionFacet == null)
1143         {
1144             return;
1145         }
1146         String captionClass;
1147         String captionStyle;
1148         if (component instanceof HtmlPanelGrid)
1149         {
1150             HtmlPanelGrid panelGrid = (HtmlPanelGrid) component;
1151             captionClass = panelGrid.getCaptionClass();
1152             captionStyle = panelGrid.getCaptionStyle();
1153         }
1154         else if (component instanceof HtmlDataTable)
1155         {
1156             HtmlDataTable dataTable = (HtmlDataTable) component;
1157             captionClass = dataTable.getCaptionClass();
1158             captionStyle = dataTable.getCaptionStyle();
1159         }
1160         else
1161         {
1162             captionClass = (String) component.getAttributes()
1163                     .get(org.apache.myfaces.shared.renderkit.JSFAttr.CAPTION_CLASS_ATTR);
1164             captionStyle = (String) component.getAttributes()
1165                     .get(org.apache.myfaces.shared.renderkit.JSFAttr.CAPTION_STYLE_ATTR);
1166         }
1167         HtmlRendererUtils.writePrettyLineSeparator(context);
1168         writer.startElement(HTML.CAPTION_ELEM, component);
1169         if (captionClass != null)
1170         {
1171             writer.writeAttribute(HTML.CLASS_ATTR, captionClass, null);
1172         }
1173 
1174         if (captionStyle != null)
1175         {
1176             writer.writeAttribute(HTML.STYLE_ATTR, captionStyle, null);
1177         }
1178         //RendererUtils.renderChild(context, captionFacet);
1179         captionFacet.encodeAll(context);
1180         writer.endElement(HTML.CAPTION_ELEM);
1181     }
1182 
1183     public static String getDisplayValueOnlyStyleClass(UIComponent component)
1184     {
1185         if (component instanceof org.apache.myfaces.shared.component.DisplayValueOnlyCapable)
1186         {
1187             if (((org.apache.myfaces.shared.component.DisplayValueOnlyCapable) component)
1188                     .getDisplayValueOnlyStyleClass() != null)
1189             {
1190                 return ((DisplayValueOnlyCapable) component)
1191                         .getDisplayValueOnlyStyleClass();
1192             }
1193             UIComponent parent = component;
1194             while ((parent = parent.getParent()) != null)
1195             {
1196                 if (parent instanceof org.apache.myfaces.shared.component.DisplayValueOnlyCapable
1197                         && ((org.apache.myfaces.shared.component.DisplayValueOnlyCapable) parent)
1198                                 .getDisplayValueOnlyStyleClass() != null)
1199                 {
1200                     return ((org.apache.myfaces.shared.component.DisplayValueOnlyCapable) parent)
1201                             .getDisplayValueOnlyStyleClass();
1202                 }
1203             }
1204         }
1205         return null;
1206     }
1207 
1208     public static String getDisplayValueOnlyStyle(UIComponent component)
1209     {
1210         if (component instanceof DisplayValueOnlyCapable)
1211         {
1212             if (((org.apache.myfaces.shared.component.DisplayValueOnlyCapable) component)
1213                     .getDisplayValueOnlyStyle() != null)
1214             {
1215                 return ((DisplayValueOnlyCapable) component)
1216                         .getDisplayValueOnlyStyle();
1217             }
1218             UIComponent parent = component;
1219             while ((parent = parent.getParent()) != null)
1220             {
1221                 if (parent instanceof org.apache.myfaces.shared.component.DisplayValueOnlyCapable
1222                         && ((DisplayValueOnlyCapable) parent)
1223                                 .getDisplayValueOnlyStyle() != null)
1224                 {
1225                     return ((DisplayValueOnlyCapable) parent)
1226                             .getDisplayValueOnlyStyle();
1227                 }
1228             }
1229         }
1230         return null;
1231     }
1232 
1233     public static boolean isDisplayValueOnly(UIComponent component)
1234     {
1235         if (component instanceof DisplayValueOnlyCapable)
1236         {
1237             if (((DisplayValueOnlyCapable) component).isSetDisplayValueOnly())
1238             {
1239                 return ((DisplayValueOnlyCapable) component).isDisplayValueOnly();
1240             }
1241             UIComponent parent = component;
1242             while ((parent = parent.getParent()) != null)
1243             {
1244                 if (parent instanceof DisplayValueOnlyCapable
1245                         && ((DisplayValueOnlyCapable) parent).isSetDisplayValueOnly())
1246                 {
1247                     return ((org.apache.myfaces.shared.component.DisplayValueOnlyCapable) parent)
1248                             .isDisplayValueOnly();
1249                 }
1250             }
1251         }
1252         return false;
1253     }
1254 
1255     public static void renderDisplayValueOnly(FacesContext facesContext,
1256             UIInput input) throws IOException
1257     {
1258         ResponseWriter writer = facesContext.getResponseWriter();
1259         writer.startElement(org.apache.myfaces.shared.renderkit.html.HTML.SPAN_ELEM, input);
1260         writeIdIfNecessary(writer, input, facesContext);
1261         renderDisplayValueOnlyAttributes(input, writer);
1262         String strValue = RendererUtils.getStringValue(facesContext, input);
1263         writer.write(HTMLEncoder.encode(strValue, true, true));
1264         writer.endElement(HTML.SPAN_ELEM);
1265     }
1266 
1267     public static void appendClearHiddenCommandFormParamsFunctionCall(
1268             StringBuilder buf, String formName)
1269     {
1270         HtmlJavaScriptUtils.appendClearHiddenCommandFormParamsFunctionCall(buf, formName);
1271     }
1272 
1273     @SuppressWarnings("unchecked")
1274     public static void renderFormSubmitScript(FacesContext facesContext)
1275             throws IOException
1276     {
1277         HtmlJavaScriptUtils.renderFormSubmitScript(facesContext);
1278     }
1279 
1280     /**
1281      * Adds the hidden form input value assignment that is necessary for the autoscroll
1282      * feature to an html link or button onclick attribute.
1283      */
1284     public static void appendAutoScrollAssignment(StringBuilder onClickValue,
1285             String formName)
1286     {
1287         HtmlJavaScriptUtils.appendAutoScrollAssignment(onClickValue, formName);
1288     }
1289 
1290     /**
1291      * Adds the hidden form input value assignment that is necessary for the autoscroll
1292      * feature to an html link or button onclick attribute.
1293      */
1294     public static void appendAutoScrollAssignment(FacesContext context,
1295             StringBuilder onClickValue, String formName)
1296     {
1297         HtmlJavaScriptUtils.appendAutoScrollAssignment(context, onClickValue, formName);
1298     }
1299 
1300     /**
1301      * Renders the hidden form input that is necessary for the autoscroll feature.
1302      */
1303     public static void renderAutoScrollHiddenInput(FacesContext facesContext,
1304             ResponseWriter writer) throws IOException
1305     {
1306         HtmlJavaScriptUtils.renderAutoScrollHiddenInput(facesContext, writer);
1307     }
1308 
1309     /**
1310      * Renders the autoscroll javascript function.
1311      */
1312     public static void renderAutoScrollFunction(FacesContext facesContext,
1313             ResponseWriter writer) throws IOException
1314     {
1315         HtmlJavaScriptUtils.renderAutoScrollFunction(facesContext, writer);
1316     }
1317 
1318     public static String getAutoScrollFunction(FacesContext facesContext)
1319     {
1320         return HtmlJavaScriptUtils.getAutoScrollFunction(facesContext);
1321     }
1322 
1323     public static boolean isAllowedCdataSection(FacesContext fc)
1324     {
1325         Boolean value = null;
1326         if (fc != null)
1327         {
1328             value = (Boolean) fc.getExternalContext().getRequestMap().get(ALLOW_CDATA_SECTION_ON);
1329         }
1330         return value != null && ((Boolean) value).booleanValue();
1331     }
1332 
1333     public static void allowCdataSection(FacesContext fc, boolean cdataSectionAllowed)
1334     {
1335         fc.getExternalContext().getRequestMap().put(ALLOW_CDATA_SECTION_ON, Boolean.valueOf(cdataSectionAllowed));
1336     }
1337 
1338     public static class LinkParameter
1339     {
1340         private String _name;
1341 
1342         private Object _value;
1343 
1344         public String getName()
1345         {
1346             return _name;
1347         }
1348 
1349         public void setName(String name)
1350         {
1351             _name = name;
1352         }
1353 
1354         public Object getValue()
1355         {
1356             return _value;
1357         }
1358 
1359         public void setValue(Object value)
1360         {
1361             _value = value;
1362         }
1363     }
1364 
1365     public static void renderHiddenCommandFormParams(ResponseWriter writer,
1366             Set dummyFormParams) throws IOException
1367     {
1368         for (Iterator it = dummyFormParams.iterator(); it.hasNext();)
1369         {
1370             Object name = it.next();
1371             renderHiddenInputField(writer, name, null);
1372         }
1373     }
1374 
1375     public static void renderHiddenInputField(ResponseWriter writer,
1376             Object name, Object value) throws IOException
1377     {
1378         writer.startElement(HTML.INPUT_ELEM, null);
1379         writer.writeAttribute(HTML.TYPE_ATTR, HTML.INPUT_TYPE_HIDDEN, null);
1380         writer.writeAttribute(HTML.NAME_ATTR, name, null);
1381         if (value != null)
1382         {
1383             writer.writeAttribute(HTML.VALUE_ATTR, value, null);
1384         }
1385         writer.endElement(HTML.INPUT_ELEM);
1386     }
1387 
1388     /**
1389      * @deprecated Replaced by
1390      *             renderLabel(ResponseWriter writer,
1391      *             UIComponent component,
1392      *             String forClientId,
1393      *             SelectItem item,
1394      *             boolean disabled).
1395      *             Renders a label HTML element
1396      */
1397     @Deprecated
1398     public static void renderLabel(ResponseWriter writer,
1399             UIComponent component, String forClientId, String labelValue,
1400             boolean disabled) throws IOException
1401     {
1402         writer.startElement(HTML.LABEL_ELEM, component);
1403         writer.writeAttribute(HTML.FOR_ATTR, forClientId, null);
1404         String labelClass = null;
1405         if (disabled)
1406         {
1407             labelClass = (String) component.getAttributes().get(JSFAttr.DISABLED_CLASS_ATTR);
1408         }
1409         else
1410         {
1411             labelClass = (String) component.getAttributes()
1412                     .get(org.apache.myfaces.shared.renderkit.JSFAttr.ENABLED_CLASS_ATTR);
1413         }
1414         if (labelClass != null)
1415         {
1416             writer.writeAttribute("class", labelClass, "labelClass");
1417         }
1418         if ((labelValue != null) && (labelValue.length() > 0))
1419         {
1420             writer.write(HTML.NBSP_ENTITY);
1421             writer.writeText(labelValue, null);
1422         }
1423         writer.endElement(HTML.LABEL_ELEM);
1424     }
1425 
1426     /**
1427      * Renders a label HTML element
1428      */
1429     public static void renderLabel(ResponseWriter writer,
1430             UIComponent component, String forClientId, SelectItem item,
1431             boolean disabled) throws IOException
1432     {
1433         writer.startElement(HTML.LABEL_ELEM, component);
1434         writer.writeAttribute(HTML.FOR_ATTR, forClientId, null);
1435         String labelClass = null;
1436         if (disabled)
1437         {
1438             labelClass = (String) component.getAttributes().get(JSFAttr.DISABLED_CLASS_ATTR);
1439         }
1440         else
1441         {
1442             labelClass = (String) component.getAttributes()
1443                     .get(org.apache.myfaces.shared.renderkit.JSFAttr.ENABLED_CLASS_ATTR);
1444         }
1445         if (labelClass != null)
1446         {
1447             writer.writeAttribute("class", labelClass, "labelClass");
1448         }
1449         if ((item.getLabel() != null) && (item.getLabel().length() > 0))
1450         {
1451             // writer.write(HTML.NBSP_ENTITY);
1452             writer.write(" ");
1453             if (item.isEscape())
1454             {
1455                 //writer.write(item.getLabel());
1456                 writer.writeText(item.getLabel(), null);
1457             }
1458             else
1459             {
1460                 //writer.write(HTMLEncoder.encode (item.getLabel()));
1461                 writer.write(item.getLabel());
1462             }
1463         }
1464         writer.endElement(HTML.LABEL_ELEM);
1465     }
1466 
1467     /**
1468      * Renders a label HTML element
1469      */
1470     public static void renderLabel(ResponseWriter writer,
1471             UIComponent component, String forClientId, SelectItem item,
1472             boolean disabled, boolean selected) throws IOException
1473     {
1474         writer.startElement(HTML.LABEL_ELEM, component);
1475         writer.writeAttribute(HTML.FOR_ATTR, forClientId, null);
1476         String labelClass = null;
1477         if (disabled)
1478         {
1479             labelClass = (String) component.getAttributes().get(JSFAttr.DISABLED_CLASS_ATTR);
1480         }
1481         else
1482         {
1483             labelClass = (String) component.getAttributes()
1484                     .get(org.apache.myfaces.shared.renderkit.JSFAttr.ENABLED_CLASS_ATTR);
1485         }
1486         String labelSelectedClass = null;
1487         if (selected)
1488         {
1489             labelSelectedClass = (String) component.getAttributes().get(JSFAttr.SELECTED_CLASS_ATTR);
1490         }
1491         else
1492         {
1493             labelSelectedClass = (String) component.getAttributes().get(JSFAttr.UNSELECTED_CLASS_ATTR);
1494         }
1495         if (labelSelectedClass != null)
1496         {
1497             if (labelClass == null)
1498             {
1499                 labelClass = labelSelectedClass;
1500             }
1501             else
1502             {
1503                 labelClass = labelClass + " " + labelSelectedClass;
1504             }
1505         }
1506         if (labelClass != null)
1507         {
1508             writer.writeAttribute("class", labelClass, "labelClass");
1509         }
1510         if ((item.getLabel() != null) && (item.getLabel().length() > 0))
1511         {
1512             writer.write(HTML.NBSP_ENTITY);
1513             if (item.isEscape())
1514             {
1515                 //writer.write(item.getLabel());
1516                 writer.writeText(item.getLabel(), null);
1517             }
1518             else
1519             {
1520                 //writer.write(HTMLEncoder.encode (item.getLabel()));
1521                 writer.write(item.getLabel());
1522             }
1523         }
1524         writer.endElement(HTML.LABEL_ELEM);
1525     }
1526 
1527     /**
1528      * Render the javascript function that is called on a click on a commandLink
1529      * to clear the hidden inputs. This is necessary because on a browser back,
1530      * each hidden input still has it's old value (browser cache!) and therefore
1531      * a new submit would cause the according action once more!
1532      *
1533      * @param writer
1534      * @param formName
1535      * @param dummyFormParams
1536      * @param formTarget
1537      * @throws IOException
1538      */
1539     public static void renderClearHiddenCommandFormParamsFunction(
1540             ResponseWriter writer, String formName, Set dummyFormParams,
1541             String formTarget) throws IOException
1542     {
1543         HtmlJavaScriptUtils.renderClearHiddenCommandFormParamsFunction(writer, formName, dummyFormParams, formTarget);
1544     }
1545 
1546     /**
1547      * Prefixes the given String with "clear_" and removes special characters
1548      *
1549      * @param formName
1550      * @return String
1551      */
1552     public static String getClearHiddenCommandFormParamsFunctionName(
1553             String formName)
1554     {
1555         return HtmlJavaScriptUtils.getClearHiddenCommandFormParamsFunctionName(formName);
1556     }
1557 
1558     public static String getClearHiddenCommandFormParamsFunctionNameMyfacesLegacy(
1559             String formName)
1560     {
1561         return HtmlJavaScriptUtils.getClearHiddenCommandFormParamsFunctionNameMyfacesLegacy(formName);
1562     }
1563 
1564     /**
1565      * Get the name of the request parameter that holds the id of the
1566      * link-type component that caused the form to be submitted.
1567      * <p/>
1568      * Within each page there may be multiple "link" type components that
1569      * cause page submission. On the server it is necessary to know which
1570      * of these actually caused the submit, in order to invoke the correct
1571      * listeners. Such components therefore store their id into the
1572      * "hidden command link field" in their associated form before
1573      * submitting it.
1574      * <p/>
1575      * The field is always a direct child of each form, and has the same
1576      * <i>name</i> in each form. The id of the form component is therefore
1577      * both necessary and sufficient to determine the full name of the
1578      * field.
1579      */
1580     public static String getHiddenCommandLinkFieldName(FormInfo formInfo)
1581     {
1582         if (RendererUtils.isAdfOrTrinidadForm(formInfo.getForm()))
1583         {
1584             return HIDDEN_COMMANDLINK_FIELD_NAME_TRINIDAD;
1585         }
1586         return formInfo.getFormName() + UINamingContainer.getSeparatorChar(FacesContext
1587                         .getCurrentInstance()) + HIDDEN_COMMANDLINK_FIELD_NAME;
1588     }
1589     
1590     public static String getHiddenCommandLinkFieldName(
1591             FormInfo formInfo, FacesContext facesContext)
1592     {
1593         if (RendererUtils.isAdfOrTrinidadForm(formInfo.getForm()))
1594         {
1595             return HIDDEN_COMMANDLINK_FIELD_NAME_TRINIDAD;
1596         }
1597         return formInfo.getFormName() + UINamingContainer.getSeparatorChar(facesContext)
1598                 + HIDDEN_COMMANDLINK_FIELD_NAME;
1599     }
1600 
1601     public static boolean isPartialOrBehaviorSubmit(FacesContext facesContext,
1602             String clientId)
1603     {
1604         Map<String, String> params = facesContext.getExternalContext().getRequestParameterMap();
1605         String sourceId = params.get("javax.faces.source");
1606         if (sourceId == null || !sourceId.equals(clientId))
1607         {
1608             return false;
1609         }
1610         boolean partialOrBehaviorSubmit = false;
1611         String behaviorEvent = params.get("javax.faces.behavior.event");
1612         if (behaviorEvent != null)
1613         {
1614             partialOrBehaviorSubmit = ClientBehaviorEvents.ACTION.equals(behaviorEvent);
1615             if (partialOrBehaviorSubmit)
1616             {
1617                 return partialOrBehaviorSubmit;
1618             }
1619         }
1620         String partialEvent = params.get("javax.faces.partial.event");
1621         if (partialEvent != null)
1622         {
1623             partialOrBehaviorSubmit = ClientBehaviorEvents.CLICK.equals(partialEvent);
1624         }
1625         return partialOrBehaviorSubmit;
1626     }
1627 
1628     public static String getHiddenCommandLinkFieldNameMyfacesOld(
1629             FormInfo formInfo)
1630     {
1631         return formInfo.getFormName() + UINamingContainer.getSeparatorChar(FacesContext.getCurrentInstance())
1632                 + HIDDEN_COMMANDLINK_FIELD_NAME_MYFACES_OLD;
1633     }
1634 
1635     public static String getOutcomeTargetHref(FacesContext facesContext,
1636             UIOutcomeTarget component) throws IOException
1637     {
1638         String outcome = component.getOutcome();
1639         outcome = (outcome == null) ? facesContext.getViewRoot().getViewId()
1640                 : outcome;
1641         outcome = ((outcome == null) ? STR_EMPTY : outcome.trim());
1642         // Get the correct URL for the outcome.
1643         NavigationHandler nh = facesContext.getApplication().getNavigationHandler();
1644         if (!(nh instanceof ConfigurableNavigationHandler))
1645         {
1646             throw new FacesException(
1647                     "Navigation handler must be an instance of "
1648                             + "ConfigurableNavigationHandler for using h:link or h:button");
1649         }
1650         ConfigurableNavigationHandler navigationHandler = (ConfigurableNavigationHandler) nh;
1651         // fromAction is null because there is no action method that was called to get the outcome
1652         NavigationCase navigationCase = navigationHandler.getNavigationCase(
1653                 facesContext, null, outcome);
1654         // when navigation case is null, force the link or button to be disabled and log a warning
1655         if (navigationCase == null)
1656         {
1657             // log a warning
1658             log.warning("Could not determine NavigationCase for UIOutcomeTarget component "
1659                     + RendererUtils.getPathToComponent(component));
1660 
1661             return null;
1662         }
1663         Map<String, List<String>> parameters = null;
1664         // handle URL parameters
1665         if (component.getChildCount() > 0)
1666         {
1667             List<UIParameter> validParams = getValidUIParameterChildren(
1668                     facesContext, component.getChildren(), true, false);
1669             if (validParams.size() > 0)
1670             {
1671                 parameters = new HashMap<String, List<String>>();
1672             }
1673             for (int i = 0, size = validParams.size(); i < size; i++)
1674             {
1675                 UIParameter param = validParams.get(i);
1676                 String name = param.getName();
1677                 Object value = param.getValue();
1678                 if (parameters.containsKey(name))
1679                 {
1680                     parameters.get(name).add(value.toString());
1681                 }
1682                 else
1683                 {
1684                     List<String> list = new ArrayList<String>(1);
1685                     list.add(value.toString());
1686                     parameters.put(name, list);
1687                 }
1688             }
1689         }
1690         // handle NavigationCase parameters
1691         Map<String, List<String>> navigationCaseParams = 
1692             NavigationUtils.getEvaluatedNavigationParameters(facesContext,
1693                 navigationCase.getParameters());
1694         if (navigationCaseParams != null)
1695         {
1696             if (parameters == null)
1697             {
1698                 parameters = new HashMap<String, List<String>>();
1699             }
1700             //parameters.putAll(navigationCaseParams);
1701             for (Map.Entry<String, List<String>> entry : navigationCaseParams
1702                     .entrySet())
1703             {
1704                 if (!parameters.containsKey(entry.getKey()))
1705                 {
1706                     parameters.put(entry.getKey(), entry.getValue());
1707                 }
1708             }
1709         }
1710         if (parameters == null)
1711         {
1712             parameters = Collections.emptyMap();
1713         }
1714         // In theory the precedence order to deal with params is this:
1715         // component parameters, navigation-case parameters, view parameters
1716         // getBookmarkableURL deal with this details.
1717         ViewHandler viewHandler = facesContext.getApplication().getViewHandler();
1718         String href = viewHandler.getBookmarkableURL(facesContext,
1719                 navigationCase.getToViewId(facesContext),
1720                 parameters, navigationCase.isIncludeViewParams() || component.isIncludeViewParams());
1721         // handle fragment (viewId#fragment)
1722         String fragment = (String) component.getAttributes().get("fragment");
1723         if (fragment != null)
1724         {
1725             fragment = fragment.trim();
1726 
1727             if (fragment.length() > 0)
1728             {
1729                 href += "#" + fragment;
1730             }
1731         }
1732         return href;
1733     }
1734 
1735     private static final String HTML_CONTENT_TYPE = "text/html";
1736     private static final String TEXT_ANY_CONTENT_TYPE = "text/*";
1737     private static final String ANY_CONTENT_TYPE = "*/*";
1738     public static final String DEFAULT_CHAR_ENCODING = "ISO-8859-1";
1739     private static final String XHTML_CONTENT_TYPE = "application/xhtml+xml";
1740     private static final String APPLICATION_XML_CONTENT_TYPE = "application/xml";
1741     private static final String TEXT_XML_CONTENT_TYPE = "text/xml";
1742     // The order is important in this case.
1743     private static final String[] SUPPORTED_CONTENT_TYPES = {
1744             HTML_CONTENT_TYPE, //Prefer this over any other, because IE does not support XHTML content type
1745             XHTML_CONTENT_TYPE, APPLICATION_XML_CONTENT_TYPE,
1746             TEXT_XML_CONTENT_TYPE, TEXT_ANY_CONTENT_TYPE, ANY_CONTENT_TYPE };
1747     /**
1748      * @deprecated use ContentTypeUtils instead
1749      */
1750     @Deprecated
1751     public static String selectContentType(String contentTypeListString)
1752     {
1753         if (contentTypeListString == null)
1754         {
1755             FacesContext context = FacesContext.getCurrentInstance();
1756             if (context != null)
1757             {
1758                 contentTypeListString = (String) context.getExternalContext()
1759                         .getRequestHeaderMap().get("Accept");
1760                 // There is a windows mobile IE client (6.12) sending
1761                 // "application/vnd.wap.mms-message;*/*"
1762                 // Note that the Accept header should be written as 
1763                 // "application/vnd.wap.mms-message,*/*" ,
1764                 // so this is bug of the client. Anyway, this is a workaround ...
1765                 if (contentTypeListString != null
1766                         && contentTypeListString.startsWith("application/vnd.wap.mms-message;*/*"))
1767                 {
1768                     contentTypeListString = "*/*";
1769                 }
1770             }
1771             if (contentTypeListString == null)
1772             {
1773                 if (log.isLoggable(Level.FINE))
1774                 {
1775                     log.fine("No content type list given, creating HtmlResponseWriterImpl with default content type.");
1776                 }
1777                 contentTypeListString = HTML_CONTENT_TYPE;
1778             }
1779         }
1780         List contentTypeList = splitContentTypeListString(contentTypeListString);
1781         String[] supportedContentTypeArray = getSupportedContentTypes();
1782         String selectedContentType = null;
1783         for (int i = 0; i < supportedContentTypeArray.length; i++)
1784         {
1785             String supportedContentType = supportedContentTypeArray[i].trim();
1786 
1787             for (int j = 0; j < contentTypeList.size(); j++)
1788             {
1789                 String contentType = (String) contentTypeList.get(j);
1790 
1791                 if (contentType.indexOf(supportedContentType) != -1)
1792                 {
1793                     if (isHTMLContentType(contentType))
1794                     {
1795                         selectedContentType = HTML_CONTENT_TYPE;
1796                     }
1797                     else if (isXHTMLContentType(contentType))
1798                     {
1799                         selectedContentType = XHTML_CONTENT_TYPE;
1800                     }
1801                     break;
1802                 }
1803             }
1804             if (selectedContentType != null)
1805             {
1806                 break;
1807             }
1808         }
1809         if (selectedContentType == null)
1810         {
1811             throw new IllegalArgumentException(
1812                     "ContentTypeList does not contain a supported content type: "
1813                             + contentTypeListString);
1814         }
1815         return selectedContentType;
1816     }
1817 
1818     public static String[] getSupportedContentTypes()
1819     {
1820         //String[] supportedContentTypeArray = new String[]{
1821         // HTML_CONTENT_TYPE,TEXT_ANY_CONTENT_TYPE,ANY_CONTENT_TYPE,
1822         // XHTML_CONTENT_TYPE,APPLICATION_XML_CONTENT_TYPE,TEXT_XML_CONTENT_TYPE};
1823         return SUPPORTED_CONTENT_TYPES;
1824     }
1825 
1826     private static boolean isHTMLContentType(String contentType)
1827     {
1828         return contentType.indexOf(HTML_CONTENT_TYPE) != -1
1829                 || contentType.indexOf(ANY_CONTENT_TYPE) != -1
1830                 || contentType.indexOf(TEXT_ANY_CONTENT_TYPE) != -1;
1831     }
1832 
1833     public static boolean isXHTMLContentType(String contentType)
1834     {
1835         return contentType.indexOf(XHTML_CONTENT_TYPE) != -1
1836                 || contentType.indexOf(APPLICATION_XML_CONTENT_TYPE) != -1
1837                 || contentType.indexOf(TEXT_XML_CONTENT_TYPE) != -1;
1838     }
1839 
1840     private static List splitContentTypeListString(String contentTypeListString)
1841     {
1842         List contentTypeList = new ArrayList();
1843         StringTokenizer st = new StringTokenizer(contentTypeListString, ",");
1844         while (st.hasMoreTokens())
1845         {
1846             String contentType = st.nextToken().trim();
1847             int semicolonIndex = contentType.indexOf(";");
1848             if (semicolonIndex != -1)
1849             {
1850                 contentType = contentType.substring(0, semicolonIndex);
1851             }
1852             contentTypeList.add(contentType);
1853         }
1854         return contentTypeList;
1855     }
1856 
1857     public static String getJavascriptLocation(UIComponent component)
1858     {
1859         if (component == null)
1860         {
1861             return null;
1862         }
1863         return (String) component.getAttributes().get(JSFAttr.JAVASCRIPT_LOCATION);
1864     }
1865 
1866     public static String getImageLocation(UIComponent component)
1867     {
1868         if (component == null)
1869         {
1870             return null;
1871         }
1872         return (String) component.getAttributes().get(JSFAttr.IMAGE_LOCATION);
1873     }
1874 
1875     public static String getStyleLocation(UIComponent component)
1876     {
1877         if (component == null)
1878         {
1879             return null;
1880         }
1881         return (String) component.getAttributes().get(JSFAttr.STYLE_LOCATION);
1882     }
1883 
1884     /**
1885      * Checks if the given component has a behavior attachment with a given name.
1886      *
1887      * @param eventName the event name to be checked for
1888      * @param behaviors map of behaviors attached to the component
1889      * @return true if client behavior with given name is attached, false otherwise
1890      * @since 4.0.0
1891      */
1892     public static boolean hasClientBehavior(String eventName,
1893             Map<String, List<ClientBehavior>> behaviors,
1894             FacesContext facesContext)
1895     {
1896         if (behaviors == null)
1897         {
1898             return false;
1899         }
1900         return (behaviors.get(eventName) != null);
1901     }
1902 
1903     public static Collection<ClientBehaviorContext.Parameter> getClientBehaviorContextParameters(
1904             Map<String, String> params)
1905     {
1906         List<ClientBehaviorContext.Parameter> paramList = null;
1907         if (params != null)
1908         {
1909             paramList = new ArrayList<ClientBehaviorContext.Parameter>(params.size());
1910             for (Map.Entry<String, String> paramEntry : params.entrySet())
1911             {
1912                 paramList.add(new ClientBehaviorContext.Parameter(paramEntry
1913                         .getKey(), paramEntry.getValue()));
1914             }
1915         }
1916         return paramList;
1917     }
1918 
1919     /**
1920      * builds the chained behavior script which then can be reused
1921      * in following order by the other script building parts
1922      * <p/>
1923      * user defined event handling script
1924      * behavior script
1925      * renderer default script
1926      *
1927      * @param eventName    event name ("onclick" etc...)
1928      * @param uiComponent  the component which has the attachement (or should have)
1929      * @param facesContext the facesContext
1930      * @param params       params map of params which have to be dragged into the request
1931      * @return a string representation of the javascripts for the attached event behavior,
1932      *         an empty string if none is present
1933      * @since 4.0.0
1934      */
1935     private static boolean getClientBehaviorScript(FacesContext facesContext,
1936             UIComponent uiComponent, String eventName,
1937             Map<String, List<ClientBehavior>> clientBehaviors,
1938             ScriptContext target,
1939             Collection<ClientBehaviorContext.Parameter> params)
1940     {
1941         return getClientBehaviorScript(facesContext, uiComponent,
1942                 uiComponent.getClientId(facesContext), eventName,
1943                 clientBehaviors, target, params);
1944     }
1945 
1946     private static boolean getClientBehaviorScript(FacesContext facesContext,
1947             UIComponent uiComponent, String targetClientId, String eventName,
1948             Map<String, List<ClientBehavior>> clientBehaviors,
1949             ScriptContext target,
1950             Collection<ClientBehaviorContext.Parameter> params)
1951     {
1952         if (!(uiComponent instanceof ClientBehaviorHolder))
1953         {
1954             target.append(STR_EMPTY);
1955             return false;
1956         }
1957         ExternalContext externalContext = facesContext.getExternalContext();
1958         boolean renderClientBehavior = JavascriptUtils
1959                 .isJavascriptAllowed(externalContext)
1960                 && clientBehaviors != null && clientBehaviors.size() > 0;
1961         if (!renderClientBehavior)
1962         {
1963             target.append(STR_EMPTY);
1964             return false;
1965         }
1966         List<ClientBehavior> attachedEventBehaviors = clientBehaviors
1967                 .get(eventName);
1968         if (attachedEventBehaviors == null
1969                 || attachedEventBehaviors.size() == 0)
1970         {
1971             target.append(STR_EMPTY);
1972             return false;
1973         }
1974         ClientBehaviorContext context = ClientBehaviorContext
1975                 .createClientBehaviorContext(facesContext, uiComponent,
1976                         eventName, targetClientId, params);
1977         boolean submitting = false;
1978         
1979         // List<ClientBehavior>  attachedEventBehaviors is  99% _DeltaList created in
1980         // javax.faces.component.UIComponentBase.addClientBehavior
1981         if (attachedEventBehaviors instanceof RandomAccess)
1982         {
1983             for (int i = 0, size = attachedEventBehaviors.size(); i < size; i++)
1984             {
1985                 ClientBehavior clientBehavior = attachedEventBehaviors.get(i);
1986                 submitting =  _appendClientBehaviourScript(target, context, 
1987                         submitting, i < (size -1), clientBehavior);   
1988             }
1989         }
1990         else 
1991         {
1992             Iterator<ClientBehavior> clientIterator = attachedEventBehaviors.iterator();
1993             while (clientIterator.hasNext())
1994             {
1995                 ClientBehavior clientBehavior = clientIterator.next();
1996                 submitting = _appendClientBehaviourScript(target, context, submitting, 
1997                         clientIterator.hasNext(), clientBehavior);
1998             }
1999         }
2000         
2001         return submitting;
2002     }
2003 
2004     private static boolean _appendClientBehaviourScript(ScriptContext target, ClientBehaviorContext context, 
2005             boolean submitting, boolean hasNext, ClientBehavior clientBehavior)
2006     {
2007         String script = clientBehavior.getScript(context);
2008         // The script _can_ be null, and in fact is for <f:ajax disabled="true" />
2009         if (script != null)
2010         {
2011             //either strings or functions, but I assume string is more appropriate 
2012             //since it allows access to the
2013             //origin as this!
2014             target.append("'" + escapeJavaScriptForChain(script) + "'");
2015             if (hasNext)
2016             {
2017                 target.append(", ");
2018             }
2019         }
2020         // MYFACES-3836 If no script provided by the client behavior, ignore the 
2021         // submitting hint because. it is evidence the client behavior is disabled.
2022         if (script != null && !submitting)
2023         {
2024             submitting = clientBehavior.getHints().contains(
2025                     ClientBehaviorHint.SUBMITTING);
2026         }
2027         return submitting;
2028     }
2029 
2030     /**
2031      * @since 4.0.0
2032      */
2033     public static String buildBehaviorChain(FacesContext facesContext,
2034             UIComponent uiComponent, String eventName,
2035             Collection<ClientBehaviorContext.Parameter> params,
2036             Map<String, List<ClientBehavior>> clientBehaviors,
2037             String userEventCode, String serverEventCode)
2038     {
2039         return buildBehaviorChain(facesContext, uiComponent,
2040                 uiComponent.getClientId(facesContext), eventName, params,
2041                 clientBehaviors, userEventCode, serverEventCode);
2042     }
2043 
2044     public static String buildBehaviorChain(FacesContext facesContext,
2045             UIComponent uiComponent, String targetClientId, String eventName,
2046             Collection<ClientBehaviorContext.Parameter> params,
2047             Map<String, List<ClientBehavior>> clientBehaviors,
2048             String userEventCode, String serverEventCode)
2049     {
2050         ExternalContext externalContext = facesContext.getExternalContext();
2051         boolean renderCode = JavascriptUtils
2052                 .isJavascriptAllowed(externalContext);
2053         if (!renderCode)
2054         {
2055             return STR_EMPTY;
2056         }
2057         List<String> finalParams = new ArrayList<String>(3);
2058         if (userEventCode != null && !userEventCode.trim().equals(STR_EMPTY))
2059         {
2060             // escape every ' in the user event code since it will
2061             // be a string attribute of jsf.util.chain
2062             finalParams.add('\'' + escapeJavaScriptForChain(userEventCode) + '\'');
2063         }
2064         final MyfacesConfig currentInstance = MyfacesConfig
2065                 .getCurrentInstance(externalContext);
2066         ScriptContext behaviorCode = new ScriptContext();
2067         ScriptContext retVal = new ScriptContext(currentInstance.isPrettyHtml());
2068         getClientBehaviorScript(facesContext, uiComponent, targetClientId,
2069                 eventName, clientBehaviors, behaviorCode, params);
2070         if (behaviorCode != null
2071                 && !behaviorCode.toString().trim().equals(STR_EMPTY))
2072         {
2073             finalParams.add(behaviorCode.toString());
2074         }
2075         if (serverEventCode != null
2076                 && !serverEventCode.trim().equals(STR_EMPTY))
2077         {
2078             finalParams
2079                     .add('\'' + escapeJavaScriptForChain(serverEventCode) + '\'');
2080         }
2081         Iterator<String> it = finalParams.iterator();
2082         // It's possible that there are no behaviors to render.  For example, if we have
2083         // <f:ajax disabled="true" /> as the only behavior.
2084         if (it.hasNext())
2085         {
2086             //according to the spec jsf.util.chain has to be used to build up the 
2087             //behavior and scripts
2088             retVal.append("jsf.util.chain(document.getElementById('"
2089                     + targetClientId + "'), event,");
2090             while (it.hasNext())
2091             {
2092                 retVal.append(it.next());
2093                 if (it.hasNext())
2094                 {
2095                     retVal.append(", ");
2096                 }
2097             }
2098             retVal.append(");");
2099         }
2100 
2101         return retVal.toString();
2102     }
2103 
2104     /**
2105      * @param facesContext
2106      * @param uiComponent
2107      * @param clientBehaviors
2108      * @param eventName1
2109      * @param eventName2
2110      * @param userEventCode
2111      * @param serverEventCode
2112      * @param params
2113      * @return
2114      * @since 4.0.0
2115      */
2116     public static String buildBehaviorChain(FacesContext facesContext,
2117             UIComponent uiComponent, String eventName1,
2118             Collection<ClientBehaviorContext.Parameter> params,
2119             String eventName2,
2120             Collection<ClientBehaviorContext.Parameter> params2,
2121             Map<String, List<ClientBehavior>> clientBehaviors,
2122             String userEventCode, String serverEventCode)
2123     {
2124         return buildBehaviorChain(facesContext, uiComponent,
2125                 uiComponent.getClientId(facesContext), eventName1, params,
2126                 eventName2, params2, clientBehaviors, userEventCode,
2127                 serverEventCode);
2128     }
2129 
2130     public static String buildBehaviorChain(FacesContext facesContext,
2131             UIComponent uiComponent, String targetClientId, String eventName1,
2132             Collection<ClientBehaviorContext.Parameter> params,
2133             String eventName2,
2134             Collection<ClientBehaviorContext.Parameter> params2,
2135             Map<String, List<ClientBehavior>> clientBehaviors,
2136             String userEventCode, String serverEventCode)
2137     {
2138         ExternalContext externalContext = facesContext.getExternalContext();
2139         boolean renderCode = JavascriptUtils
2140                 .isJavascriptAllowed(externalContext);
2141         if (!renderCode)
2142         {
2143             return STR_EMPTY;
2144         }
2145         List<String> finalParams = new ArrayList<String>(3);
2146         if (userEventCode != null && !userEventCode.trim().equals(STR_EMPTY))
2147         {
2148             finalParams.add('\'' + escapeJavaScriptForChain(userEventCode) + '\'');
2149         }
2150 
2151         final MyfacesConfig currentInstance = MyfacesConfig
2152                 .getCurrentInstance(externalContext);
2153         ScriptContext behaviorCode = new ScriptContext();
2154         ScriptContext retVal = new ScriptContext(currentInstance.isPrettyHtml());
2155         boolean submitting1 = getClientBehaviorScript(facesContext,
2156                 uiComponent, targetClientId, eventName1, clientBehaviors,
2157                 behaviorCode, params);
2158         ScriptContext behaviorCode2 = new ScriptContext();
2159         boolean submitting2 = getClientBehaviorScript(facesContext,
2160                 uiComponent, targetClientId, eventName2, clientBehaviors,
2161                 behaviorCode2, params2);
2162 
2163         // ClientBehaviors for both events have to be checked for the Submitting hint
2164         boolean submitting = submitting1 || submitting2;
2165         if (behaviorCode != null
2166                 && !behaviorCode.toString().trim().equals(STR_EMPTY))
2167         {
2168             finalParams.add(behaviorCode.toString());
2169         }
2170         if (behaviorCode2 != null
2171                 && !behaviorCode2.toString().trim().equals(STR_EMPTY))
2172         {
2173             finalParams.add(behaviorCode2.toString());
2174         }
2175         if (serverEventCode != null
2176                 && !serverEventCode.trim().equals(STR_EMPTY))
2177         {
2178             finalParams.add('\'' + escapeJavaScriptForChain(serverEventCode) + '\'');
2179         }
2180         
2181         // It's possible that there are no behaviors to render.  For example, if we have
2182         // <f:ajax disabled="true" /> as the only behavior.
2183         
2184         int size = finalParams.size();
2185         if (size > 0)
2186         {
2187             if (!submitting)
2188             {
2189                 retVal.append("return ");
2190             }
2191             //according to the spec jsf.util.chain has to be used to build up the 
2192             //behavior and scripts
2193             retVal.append("jsf.util.chain(document.getElementById('"
2194                     + targetClientId + "'), event,");
2195             int cursor = 0;
2196             while (cursor != size)
2197             {
2198                 retVal.append(finalParams.get(cursor));
2199                 cursor++;
2200                 if (cursor != size)
2201                 {
2202                     retVal.append(", ");
2203                 }
2204             }
2205             retVal.append(");");
2206             if (submitting)
2207             {
2208                 retVal.append(" return false;");
2209             }
2210         }
2211 
2212         return retVal.toString();
2213 
2214     }
2215 
2216     /**
2217      * This function correctly escapes the given JavaScript code
2218      * for the use in the jsf.util.chain() JavaScript function.
2219      * It also handles double-escaping correclty.
2220      *
2221      * @param javaScript
2222      * @return
2223      */
2224     public static String escapeJavaScriptForChain(String javaScript)
2225     {
2226         return HtmlJavaScriptUtils.escapeJavaScriptForChain(javaScript);
2227     }
2228 
2229     public static Map<String, String> mapAttachedParamsToStringValues(
2230             FacesContext facesContext, UIComponent uiComponent)
2231     {
2232         Map<String, String> retVal = null;
2233         if (uiComponent.getChildCount() > 0)
2234         {
2235             List<UIParameter> validParams = getValidUIParameterChildren(
2236                     facesContext, uiComponent.getChildren(), true, true);
2237             for (int i = 0, size = validParams.size(); i < size; i++)
2238             {
2239                 UIParameter param = validParams.get(i);
2240                 String name = param.getName();
2241                 Object value = param.getValue();
2242                 if (retVal == null)
2243                 {
2244                     retVal = new HashMap<String, String>();
2245                 }
2246                 if (value instanceof String)
2247                 {
2248                     retVal.put(name, (String) value);
2249                 }
2250                 else
2251                 {
2252                     retVal.put(name, value.toString());
2253                 }
2254             }
2255         }
2256         if (retVal == null)
2257         {
2258             retVal = Collections.emptyMap();
2259         }
2260         return retVal;
2261     }
2262 
2263     /**
2264      * Calls getValidUIParameterChildren(facesContext, children, skipNullValue, skipUnrendered, true);
2265      *
2266      * @param facesContext
2267      * @param children
2268      * @param skipNullValue
2269      * @param skipUnrendered
2270      * @return ArrayList size > 0 if any parameter found
2271      */
2272     public static List<UIParameter> getValidUIParameterChildren(
2273             FacesContext facesContext, List<UIComponent> children,
2274             boolean skipNullValue, boolean skipUnrendered)
2275     {
2276         return getValidUIParameterChildren(facesContext, children,
2277                 skipNullValue, skipUnrendered, true);
2278     }
2279 
2280     /**
2281      * Returns a List of all valid UIParameter children from the given children.
2282      * Valid means that the UIParameter is not disabled, its name is not null
2283      * (if skipNullName is true), its value is not null (if skipNullValue is true)
2284      * and it is rendered (if skipUnrendered is true). This method also creates a
2285      * warning for every UIParameter with a null-name (again, if skipNullName is true)
2286      * and, if ProjectStage is Development and skipNullValue is true, it informs the
2287      * user about every null-value.
2288      *
2289      * @param facesContext
2290      * @param children
2291      * @param skipNullValue  should UIParameters with a null value be skipped
2292      * @param skipUnrendered should UIParameters with isRendered() returning false be skipped
2293      * @param skipNullName   should UIParameters with a null name be skipped
2294      *                       (normally true, but in the case of h:outputFormat false)
2295      * @return ArrayList size > 0 if any parameter found 
2296      */
2297     public static List<UIParameter> getValidUIParameterChildren(
2298             FacesContext facesContext, List<UIComponent> children,
2299             boolean skipNullValue, boolean skipUnrendered, boolean skipNullName)
2300     {
2301         List<UIParameter> params = null;
2302         for (int i = 0, size = children.size(); i < size; i++)
2303         {
2304             UIComponent child = children.get(i);
2305             if (child instanceof UIParameter)
2306             {
2307                 UIParameter param = (UIParameter) child;
2308                 // check for the disable attribute (since 2.0)
2309                 // and the render attribute (only if skipUnrendered is true)
2310                 if (param.isDisable() || (skipUnrendered && !param.isRendered()))
2311                 {
2312                     // ignore this UIParameter and continue
2313                     continue;
2314                 }
2315                 // check the name
2316                 String name = param.getName();
2317                 if (skipNullName && (name == null || STR_EMPTY.equals(name)))
2318                 {
2319                     // warn for a null-name
2320                     log.log(Level.WARNING, "The UIParameter " + RendererUtils.getPathToComponent(param)
2321                                     + " has a name of null or empty string and thus will not be added to the URL.");
2322                     // and skip it
2323                     continue;
2324                 }
2325                 // check the value
2326                 if (skipNullValue && param.getValue() == null)
2327                 {
2328                     if (facesContext.isProjectStage(ProjectStage.Development))
2329                     {
2330                         // inform the user about the null value when in Development stage
2331                         log.log(Level.INFO, "The UIParameter " + RendererUtils.getPathToComponent(param)
2332                                         + " has a value of null and thus will not be added to the URL.");
2333                     }
2334                     // skip a null-value
2335                     continue;
2336                 }
2337                 // add the param
2338                 if (params == null)
2339                 {
2340                     params = new ArrayList<UIParameter>();
2341                 }
2342                 params.add(param);
2343             }
2344         }
2345         if (params == null)
2346         {
2347             params = Collections.emptyList();
2348         }
2349         return params;
2350     }
2351 
2352     /**
2353      * Render an attribute taking into account the passed event and
2354      * the component property. It will be rendered as "componentProperty"
2355      * attribute.
2356      *
2357      * @param facesContext
2358      * @param writer
2359      * @param componentProperty
2360      * @param component
2361      * @param eventName
2362      * @param clientBehaviors
2363      * @return
2364      * @throws IOException
2365      * @since 4.0.1
2366      */
2367     public static boolean renderBehaviorizedAttribute(
2368             FacesContext facesContext, ResponseWriter writer,
2369             String componentProperty, UIComponent component, String eventName,
2370             Map<String, List<ClientBehavior>> clientBehaviors)
2371             throws IOException
2372     {
2373         return renderBehaviorizedAttribute(facesContext, writer,
2374                 componentProperty, component, eventName, clientBehaviors, componentProperty);
2375     }
2376 
2377     public static boolean renderBehaviorizedAttribute(
2378             FacesContext facesContext, ResponseWriter writer,
2379             String componentProperty, UIComponent component,
2380             String targetClientId, String eventName,
2381             Map<String, List<ClientBehavior>> clientBehaviors)
2382             throws IOException
2383     {
2384         return renderBehaviorizedAttribute(facesContext, writer,
2385                 componentProperty, component, targetClientId, eventName, clientBehaviors, componentProperty);
2386     }
2387 
2388     /**
2389      * Render an attribute taking into account the passed event and
2390      * the component property. The event will be rendered on the selected
2391      * htmlAttrName
2392      *
2393      * @param facesContext
2394      * @param writer
2395      * @param component
2396      * @param clientBehaviors
2397      * @param eventName
2398      * @param componentProperty
2399      * @param htmlAttrName
2400      * @return
2401      * @throws IOException
2402      * @since 4.0.1
2403      */
2404     public static boolean renderBehaviorizedAttribute(
2405             FacesContext facesContext, ResponseWriter writer,
2406             String componentProperty, UIComponent component, String eventName,
2407             Map<String, List<ClientBehavior>> clientBehaviors,
2408             String htmlAttrName) throws IOException
2409     {
2410         return renderBehaviorizedAttribute(facesContext, writer,
2411                 componentProperty, component, eventName, null, clientBehaviors,
2412                 htmlAttrName, (String) component.getAttributes().get(componentProperty));
2413     }
2414 
2415     public static boolean renderBehaviorizedAttribute(
2416             FacesContext facesContext, ResponseWriter writer, String componentProperty, UIComponent component,
2417             String targetClientId, String eventName, Map<String, List<ClientBehavior>> clientBehaviors,
2418             String htmlAttrName) throws IOException
2419     {
2420         return renderBehaviorizedAttribute(facesContext, writer,
2421                 componentProperty, component, targetClientId, eventName, null,
2422                 clientBehaviors, htmlAttrName, (String) component.getAttributes().get(componentProperty));
2423     }
2424 
2425     /**
2426      * Render an attribute taking into account the passed event,
2427      * the component property and the passed attribute value for the component
2428      * property. The event will be rendered on the selected htmlAttrName.
2429      *
2430      * @param facesContext
2431      * @param writer
2432      * @param componentProperty
2433      * @param component
2434      * @param eventName
2435      * @param clientBehaviors
2436      * @param htmlAttrName
2437      * @param attributeValue
2438      * @return
2439      * @throws IOException
2440      */
2441     public static boolean renderBehaviorizedAttribute(
2442             FacesContext facesContext, ResponseWriter writer,
2443             String componentProperty, UIComponent component, String eventName,
2444             Collection<ClientBehaviorContext.Parameter> eventParameters,
2445             Map<String, List<ClientBehavior>> clientBehaviors,
2446             String htmlAttrName, String attributeValue) throws IOException
2447     {
2448         return renderBehaviorizedAttribute(facesContext, writer,
2449                 componentProperty, component,
2450                 component.getClientId(facesContext), eventName,
2451                 eventParameters, clientBehaviors, htmlAttrName, attributeValue);
2452     }
2453 
2454     public static boolean renderBehaviorizedAttribute(
2455             FacesContext facesContext, ResponseWriter writer,
2456             String componentProperty, UIComponent component,
2457             String targetClientId, String eventName,
2458             Collection<ClientBehaviorContext.Parameter> eventParameters,
2459             Map<String, List<ClientBehavior>> clientBehaviors,
2460             String htmlAttrName, String attributeValue) throws IOException
2461     {
2462 
2463         List<ClientBehavior> cbl = (clientBehaviors != null) ? clientBehaviors
2464                 .get(eventName) : null;
2465         if (cbl == null || cbl.size() == 0)
2466         {
2467             return renderHTMLAttribute(writer, componentProperty, htmlAttrName,
2468                     attributeValue);
2469         }
2470         if (cbl.size() > 1 || (cbl.size() == 1 && attributeValue != null))
2471         {
2472             return renderHTMLAttribute(writer, componentProperty, htmlAttrName,
2473                     HtmlRendererUtils.buildBehaviorChain(facesContext,
2474                             component, targetClientId, eventName,
2475                             eventParameters, clientBehaviors, attributeValue,
2476                             STR_EMPTY));
2477         }
2478         else
2479         {
2480             //Only 1 behavior and attrValue == null, so just render it directly
2481             return renderHTMLAttribute(
2482                     writer, componentProperty, htmlAttrName,
2483                     cbl.get(0).getScript(
2484                             ClientBehaviorContext.createClientBehaviorContext(
2485                                     facesContext, component, eventName,
2486                                     targetClientId, eventParameters)));
2487         }
2488     }
2489 
2490     /**
2491      * Render an attribute taking into account the passed event,
2492      * the passed attribute value for the component property.
2493      * and the specific server code.
2494      * The event will be rendered on the selected htmlAttrName.
2495      *
2496      * @param facesContext
2497      * @param writer
2498      * @param componentProperty
2499      * @param component
2500      * @param eventName
2501      * @param clientBehaviors
2502      * @param htmlAttrName
2503      * @param attributeValue
2504      * @param serverSideScript
2505      * @return
2506      * @throws IOException
2507      */
2508     public static boolean renderBehaviorizedAttribute(
2509             FacesContext facesContext, ResponseWriter writer,
2510             String componentProperty, UIComponent component, String eventName,
2511             Collection<ClientBehaviorContext.Parameter> eventParameters,
2512             Map<String, List<ClientBehavior>> clientBehaviors,
2513             String htmlAttrName, String attributeValue, String serverSideScript)
2514             throws IOException
2515     {
2516         return renderBehaviorizedAttribute(facesContext, writer,
2517                 componentProperty, component,
2518                 component.getClientId(facesContext), eventName,
2519                 eventParameters, clientBehaviors, htmlAttrName, attributeValue,
2520                 serverSideScript);
2521     }
2522 
2523     // CHECKSTYLE:OFF
2524     public static boolean renderBehaviorizedAttribute(
2525             FacesContext facesContext, ResponseWriter writer,
2526             String componentProperty, UIComponent component,
2527             String targetClientId, String eventName,
2528             Collection<ClientBehaviorContext.Parameter> eventParameters,
2529             Map<String, List<ClientBehavior>> clientBehaviors,
2530             String htmlAttrName, String attributeValue, String serverSideScript)
2531             throws IOException
2532     {
2533 
2534         List<ClientBehavior> cbl = (clientBehaviors != null) ? clientBehaviors
2535                 .get(eventName) : null;
2536         if (((cbl != null) ? cbl.size() : 0) + (attributeValue != null ? 1 : 0)
2537                 + (serverSideScript != null ? 1 : 0) <= 1)
2538         {
2539             if (cbl == null || cbl.size() == 0)
2540             {
2541                 if (attributeValue != null)
2542                 {
2543                     return renderHTMLStringAttribute(writer, componentProperty,
2544                             htmlAttrName, attributeValue);
2545                 }
2546                 else
2547                 {
2548                     return renderHTMLStringAttribute(writer, componentProperty,
2549                             htmlAttrName, serverSideScript);
2550                 }
2551             }
2552             else
2553             {
2554                 return renderHTMLStringAttribute(
2555                         writer, componentProperty, htmlAttrName,
2556                         cbl.get(0).getScript(
2557                                 ClientBehaviorContext
2558                                         .createClientBehaviorContext(
2559                                                 facesContext, component,
2560                                                 eventName, targetClientId,
2561                                                 eventParameters)));
2562             }
2563         }
2564         else
2565         {
2566             return renderHTMLStringAttribute(writer, componentProperty, htmlAttrName,
2567                     HtmlRendererUtils.buildBehaviorChain(facesContext,
2568                             component, targetClientId, eventName,
2569                             eventParameters, clientBehaviors, attributeValue,
2570                             serverSideScript));
2571         }
2572     }
2573 
2574     public static boolean renderBehaviorizedAttribute(
2575             FacesContext facesContext, ResponseWriter writer,
2576             String componentProperty, UIComponent component, String eventName,
2577             Collection<ClientBehaviorContext.Parameter> eventParameters,
2578             String eventName2,
2579             Collection<ClientBehaviorContext.Parameter> eventParameters2,
2580             Map<String, List<ClientBehavior>> clientBehaviors,
2581             String htmlAttrName, String attributeValue, String serverSideScript)
2582             throws IOException
2583     {
2584         return renderBehaviorizedAttribute(facesContext, writer,
2585                 componentProperty, component,
2586                 component.getClientId(facesContext), eventName,
2587                 eventParameters, eventName2, eventParameters2, clientBehaviors,
2588                 htmlAttrName, attributeValue, serverSideScript);
2589     }
2590 
2591     public static boolean renderBehaviorizedAttribute(
2592             FacesContext facesContext, ResponseWriter writer,
2593             String componentProperty, UIComponent component,
2594             String targetClientId, String eventName,
2595             Collection<ClientBehaviorContext.Parameter> eventParameters,
2596             String eventName2,
2597             Collection<ClientBehaviorContext.Parameter> eventParameters2,
2598             Map<String, List<ClientBehavior>> clientBehaviors,
2599             String htmlAttrName, String attributeValue, String serverSideScript)
2600             throws IOException
2601     {
2602         List<ClientBehavior> cb1 = (clientBehaviors != null) ? clientBehaviors
2603                 .get(eventName) : null;
2604         List<ClientBehavior> cb2 = (clientBehaviors != null) ? clientBehaviors
2605                 .get(eventName2) : null;
2606         if (((cb1 != null) ? cb1.size() : 0) + ((cb2 != null) ? cb2.size() : 0)
2607                 + (attributeValue != null ? 1 : 0) <= 1)
2608         {
2609             if (attributeValue != null)
2610             {
2611                 return renderHTMLStringAttribute(writer, componentProperty,
2612                         htmlAttrName, attributeValue);
2613             }
2614             else if (serverSideScript != null)
2615             {
2616                 return renderHTMLStringAttribute(writer, componentProperty,
2617                         htmlAttrName, serverSideScript);
2618             }
2619             else if (((cb1 != null) ? cb1.size() : 0) > 0)
2620             {
2621                 return renderHTMLStringAttribute(
2622                         writer, componentProperty, htmlAttrName,
2623                         cb1.get(0).getScript(ClientBehaviorContext
2624                                         .createClientBehaviorContext(
2625                                                 facesContext, component,
2626                                                 eventName, targetClientId,
2627                                                 eventParameters)));
2628             }
2629             else
2630             {
2631                 return renderHTMLStringAttribute(
2632                         writer, componentProperty, htmlAttrName,
2633                         cb2.get(0).getScript(ClientBehaviorContext
2634                                         .createClientBehaviorContext(
2635                                                 facesContext, component,
2636                                                 eventName2, targetClientId,
2637                                                 eventParameters2)));
2638             }
2639         }
2640         else
2641         {
2642             return renderHTMLStringAttribute(writer, componentProperty, htmlAttrName,
2643                     HtmlRendererUtils.buildBehaviorChain(facesContext,
2644                             component, targetClientId, eventName,
2645                             eventParameters, eventName2, eventParameters2,
2646                             clientBehaviors, attributeValue, serverSideScript));
2647         }
2648     }
2649     // CHECKSTYLE: ON
2650     
2651     /**
2652      * @since 4.0.0
2653      */
2654     public static void renderBehaviorizedEventHandlers(
2655             FacesContext facesContext, ResponseWriter writer,
2656             UIComponent uiComponent,
2657             Map<String, List<ClientBehavior>> clientBehaviors)
2658             throws IOException
2659     {
2660         renderBehaviorizedEventHandlers(facesContext, writer, uiComponent,
2661                 uiComponent.getClientId(facesContext), clientBehaviors);
2662     }
2663 
2664     public static void renderBehaviorizedEventHandlers(
2665             FacesContext facesContext, ResponseWriter writer,
2666             UIComponent uiComponent, String targetClientId,
2667             Map<String, List<ClientBehavior>> clientBehaviors)
2668             throws IOException
2669     {
2670         renderBehaviorizedAttribute(facesContext, writer, HTML.ONCLICK_ATTR,
2671                 uiComponent, targetClientId, ClientBehaviorEvents.CLICK,
2672                 clientBehaviors, HTML.ONCLICK_ATTR);
2673         renderBehaviorizedAttribute(facesContext, writer, HTML.ONDBLCLICK_ATTR,
2674                 uiComponent, targetClientId, ClientBehaviorEvents.DBLCLICK,
2675                 clientBehaviors, HTML.ONDBLCLICK_ATTR);
2676         renderBehaviorizedAttribute(facesContext, writer,
2677                 HTML.ONMOUSEDOWN_ATTR, uiComponent, targetClientId,
2678                 ClientBehaviorEvents.MOUSEDOWN, clientBehaviors,
2679                 HTML.ONMOUSEDOWN_ATTR);
2680         renderBehaviorizedAttribute(facesContext, writer, HTML.ONMOUSEUP_ATTR,
2681                 uiComponent, targetClientId, ClientBehaviorEvents.MOUSEUP,
2682                 clientBehaviors, HTML.ONMOUSEUP_ATTR);
2683         renderBehaviorizedAttribute(facesContext, writer,
2684                 HTML.ONMOUSEOVER_ATTR, uiComponent, targetClientId,
2685                 ClientBehaviorEvents.MOUSEOVER, clientBehaviors,
2686                 HTML.ONMOUSEOVER_ATTR);
2687         renderBehaviorizedAttribute(facesContext, writer,
2688                 HTML.ONMOUSEMOVE_ATTR, uiComponent, targetClientId,
2689                 ClientBehaviorEvents.MOUSEMOVE, clientBehaviors,
2690                 HTML.ONMOUSEMOVE_ATTR);
2691         renderBehaviorizedAttribute(facesContext, writer, HTML.ONMOUSEOUT_ATTR,
2692                 uiComponent, targetClientId, ClientBehaviorEvents.MOUSEOUT,
2693                 clientBehaviors, HTML.ONMOUSEOUT_ATTR);
2694         renderBehaviorizedAttribute(facesContext, writer, HTML.ONKEYPRESS_ATTR,
2695                 uiComponent, targetClientId, ClientBehaviorEvents.KEYPRESS,
2696                 clientBehaviors, HTML.ONKEYPRESS_ATTR);
2697         renderBehaviorizedAttribute(facesContext, writer, HTML.ONKEYDOWN_ATTR,
2698                 uiComponent, targetClientId, ClientBehaviorEvents.KEYDOWN,
2699                 clientBehaviors, HTML.ONKEYDOWN_ATTR);
2700         renderBehaviorizedAttribute(facesContext, writer, HTML.ONKEYUP_ATTR,
2701                 uiComponent, targetClientId, ClientBehaviorEvents.KEYUP,
2702                 clientBehaviors, HTML.ONKEYUP_ATTR);
2703     }
2704 
2705     /**
2706      * @since 4.0.0
2707      */
2708     public static void renderBehaviorizedEventHandlersWithoutOnclick(
2709             FacesContext facesContext, ResponseWriter writer,
2710             UIComponent uiComponent,
2711             Map<String, List<ClientBehavior>> clientBehaviors)
2712             throws IOException
2713     {
2714         renderBehaviorizedAttribute(facesContext, writer, HTML.ONDBLCLICK_ATTR,
2715                 uiComponent, ClientBehaviorEvents.DBLCLICK, clientBehaviors,
2716                 HTML.ONDBLCLICK_ATTR);
2717         renderBehaviorizedAttribute(facesContext, writer,
2718                 HTML.ONMOUSEDOWN_ATTR, uiComponent,
2719                 ClientBehaviorEvents.MOUSEDOWN, clientBehaviors,
2720                 HTML.ONMOUSEDOWN_ATTR);
2721         renderBehaviorizedAttribute(facesContext, writer, HTML.ONMOUSEUP_ATTR,
2722                 uiComponent, ClientBehaviorEvents.MOUSEUP, clientBehaviors,
2723                 HTML.ONMOUSEUP_ATTR);
2724         renderBehaviorizedAttribute(facesContext, writer,
2725                 HTML.ONMOUSEOVER_ATTR, uiComponent,
2726                 ClientBehaviorEvents.MOUSEOVER, clientBehaviors,
2727                 HTML.ONMOUSEOVER_ATTR);
2728         renderBehaviorizedAttribute(facesContext, writer,
2729                 HTML.ONMOUSEMOVE_ATTR, uiComponent,
2730                 ClientBehaviorEvents.MOUSEMOVE, clientBehaviors,
2731                 HTML.ONMOUSEMOVE_ATTR);
2732         renderBehaviorizedAttribute(facesContext, writer, HTML.ONMOUSEOUT_ATTR,
2733                 uiComponent, ClientBehaviorEvents.MOUSEOUT, clientBehaviors,
2734                 HTML.ONMOUSEOUT_ATTR);
2735         renderBehaviorizedAttribute(facesContext, writer, HTML.ONKEYPRESS_ATTR,
2736                 uiComponent, ClientBehaviorEvents.KEYPRESS, clientBehaviors,
2737                 HTML.ONKEYPRESS_ATTR);
2738         renderBehaviorizedAttribute(facesContext, writer, HTML.ONKEYDOWN_ATTR,
2739                 uiComponent, ClientBehaviorEvents.KEYDOWN, clientBehaviors,
2740                 HTML.ONKEYDOWN_ATTR);
2741         renderBehaviorizedAttribute(facesContext, writer, HTML.ONKEYUP_ATTR,
2742                 uiComponent, ClientBehaviorEvents.KEYUP, clientBehaviors,
2743                 HTML.ONKEYUP_ATTR);
2744     }
2745 
2746     public static void renderBehaviorizedEventHandlersWithoutOnmouseoverAndOnmouseout(
2747             FacesContext facesContext, ResponseWriter writer,
2748             UIComponent uiComponent,
2749             Map<String, List<ClientBehavior>> clientBehaviors)
2750             throws IOException
2751     {
2752         renderBehaviorizedAttribute(facesContext, writer, HTML.ONCLICK_ATTR,
2753                 uiComponent, ClientBehaviorEvents.CLICK, clientBehaviors,
2754                 HTML.ONCLICK_ATTR);
2755         renderBehaviorizedAttribute(facesContext, writer, HTML.ONDBLCLICK_ATTR,
2756                 uiComponent, ClientBehaviorEvents.DBLCLICK, clientBehaviors,
2757                 HTML.ONDBLCLICK_ATTR);
2758         renderBehaviorizedAttribute(facesContext, writer,
2759                 HTML.ONMOUSEDOWN_ATTR, uiComponent,
2760                 ClientBehaviorEvents.MOUSEDOWN, clientBehaviors,
2761                 HTML.ONMOUSEDOWN_ATTR);
2762         renderBehaviorizedAttribute(facesContext, writer, HTML.ONMOUSEUP_ATTR,
2763                 uiComponent, ClientBehaviorEvents.MOUSEUP, clientBehaviors,
2764                 HTML.ONMOUSEUP_ATTR);
2765         renderBehaviorizedAttribute(facesContext, writer,
2766                 HTML.ONMOUSEMOVE_ATTR, uiComponent,
2767                 ClientBehaviorEvents.MOUSEMOVE, clientBehaviors,
2768                 HTML.ONMOUSEMOVE_ATTR);
2769         renderBehaviorizedAttribute(facesContext, writer, HTML.ONKEYPRESS_ATTR,
2770                 uiComponent, ClientBehaviorEvents.KEYPRESS, clientBehaviors,
2771                 HTML.ONKEYPRESS_ATTR);
2772         renderBehaviorizedAttribute(facesContext, writer, HTML.ONKEYDOWN_ATTR,
2773                 uiComponent, ClientBehaviorEvents.KEYDOWN, clientBehaviors,
2774                 HTML.ONKEYDOWN_ATTR);
2775         renderBehaviorizedAttribute(facesContext, writer, HTML.ONKEYUP_ATTR,
2776                 uiComponent, ClientBehaviorEvents.KEYUP, clientBehaviors,
2777                 HTML.ONKEYUP_ATTR);
2778     }
2779 
2780     /**
2781      * @since 4.0.0
2782      */
2783     public static void renderBehaviorizedFieldEventHandlers(
2784             FacesContext facesContext, ResponseWriter writer,
2785             UIComponent uiComponent,
2786             Map<String, List<ClientBehavior>> clientBehaviors)
2787             throws IOException
2788     {
2789         renderBehaviorizedAttribute(facesContext, writer, HTML.ONFOCUS_ATTR,
2790                 uiComponent, ClientBehaviorEvents.FOCUS, clientBehaviors, HTML.ONFOCUS_ATTR);
2791         renderBehaviorizedAttribute(facesContext, writer, HTML.ONBLUR_ATTR,
2792                 uiComponent, ClientBehaviorEvents.BLUR, clientBehaviors, HTML.ONBLUR_ATTR);
2793         renderBehaviorizedAttribute(facesContext, writer, HTML.ONCHANGE_ATTR,
2794                 uiComponent, ClientBehaviorEvents.CHANGE, clientBehaviors, HTML.ONCHANGE_ATTR);
2795         renderBehaviorizedAttribute(facesContext, writer, HTML.ONSELECT_ATTR,
2796                 uiComponent, ClientBehaviorEvents.SELECT, clientBehaviors, HTML.ONSELECT_ATTR);
2797     }
2798 
2799     public static void renderBehaviorizedFieldEventHandlersWithoutOnfocus(
2800             FacesContext facesContext, ResponseWriter writer,
2801             UIComponent uiComponent,
2802             Map<String, List<ClientBehavior>> clientBehaviors)
2803             throws IOException
2804     {
2805         renderBehaviorizedAttribute(facesContext, writer, HTML.ONBLUR_ATTR,
2806                 uiComponent, ClientBehaviorEvents.BLUR, clientBehaviors, HTML.ONBLUR_ATTR);
2807         renderBehaviorizedAttribute(facesContext, writer, HTML.ONCHANGE_ATTR,
2808                 uiComponent, ClientBehaviorEvents.CHANGE, clientBehaviors, HTML.ONCHANGE_ATTR);
2809         renderBehaviorizedAttribute(facesContext, writer, HTML.ONSELECT_ATTR,
2810                 uiComponent, ClientBehaviorEvents.SELECT, clientBehaviors, HTML.ONSELECT_ATTR);
2811     }
2812 
2813     /**
2814      * @since 4.0.0
2815      */
2816     public static void renderBehaviorizedFieldEventHandlersWithoutOnchange(
2817             FacesContext facesContext, ResponseWriter writer,
2818             UIComponent uiComponent,
2819             Map<String, List<ClientBehavior>> clientBehaviors)
2820             throws IOException
2821     {
2822         renderBehaviorizedAttribute(facesContext, writer, HTML.ONFOCUS_ATTR,
2823                 uiComponent, ClientBehaviorEvents.FOCUS, clientBehaviors, HTML.ONFOCUS_ATTR);
2824         renderBehaviorizedAttribute(facesContext, writer, HTML.ONBLUR_ATTR,
2825                 uiComponent, ClientBehaviorEvents.BLUR, clientBehaviors, HTML.ONBLUR_ATTR);
2826         renderBehaviorizedAttribute(facesContext, writer, HTML.ONSELECT_ATTR,
2827                 uiComponent, ClientBehaviorEvents.SELECT, clientBehaviors, HTML.ONSELECT_ATTR);
2828     }
2829 
2830     public static void renderBehaviorizedFieldEventHandlersWithoutOnchange(
2831             FacesContext facesContext, ResponseWriter writer,
2832             UIComponent uiComponent, String targetClientId,
2833             Map<String, List<ClientBehavior>> clientBehaviors)
2834             throws IOException
2835     {
2836         renderBehaviorizedAttribute(facesContext, writer, HTML.ONFOCUS_ATTR,
2837                 uiComponent, targetClientId, ClientBehaviorEvents.FOCUS, clientBehaviors, HTML.ONFOCUS_ATTR);
2838         renderBehaviorizedAttribute(facesContext, writer, HTML.ONBLUR_ATTR,
2839                 uiComponent, targetClientId, ClientBehaviorEvents.BLUR, clientBehaviors, HTML.ONBLUR_ATTR);
2840         renderBehaviorizedAttribute(facesContext, writer, HTML.ONSELECT_ATTR,
2841                 uiComponent, targetClientId, ClientBehaviorEvents.SELECT, clientBehaviors, HTML.ONSELECT_ATTR);
2842     }
2843 
2844     public static void renderBehaviorizedFieldEventHandlersWithoutOnchangeAndOnselect(
2845             FacesContext facesContext, ResponseWriter writer,
2846             UIComponent uiComponent,
2847             Map<String, List<ClientBehavior>> clientBehaviors)
2848             throws IOException
2849     {
2850         renderBehaviorizedAttribute(facesContext, writer, HTML.ONFOCUS_ATTR,
2851                 uiComponent, ClientBehaviorEvents.FOCUS, clientBehaviors, HTML.ONFOCUS_ATTR);
2852         renderBehaviorizedAttribute(facesContext, writer, HTML.ONBLUR_ATTR,
2853                 uiComponent, ClientBehaviorEvents.BLUR, clientBehaviors, HTML.ONBLUR_ATTR);
2854     }
2855 
2856     /**
2857      * @since 4.0.0
2858      */
2859     public static boolean renderBehaviorizedOnchangeEventHandler(
2860             FacesContext facesContext, ResponseWriter writer,
2861             UIComponent uiComponent,
2862             Map<String, List<ClientBehavior>> clientBehaviors)
2863             throws IOException
2864     {
2865         boolean hasChange = HtmlRendererUtils.hasClientBehavior(
2866                 ClientBehaviorEvents.CHANGE, clientBehaviors, facesContext);
2867         boolean hasValueChange = HtmlRendererUtils
2868                 .hasClientBehavior(ClientBehaviorEvents.VALUECHANGE,
2869                         clientBehaviors, facesContext);
2870 
2871         if (hasChange && hasValueChange)
2872         {
2873             String chain = HtmlRendererUtils.buildBehaviorChain(facesContext,
2874                     uiComponent, ClientBehaviorEvents.CHANGE, null,
2875                     ClientBehaviorEvents.VALUECHANGE, null, clientBehaviors,
2876                     (String) uiComponent.getAttributes().get(HTML.ONCHANGE_ATTR), null);
2877 
2878             return HtmlRendererUtils.renderHTMLStringAttribute(writer,
2879                     HTML.ONCHANGE_ATTR, HTML.ONCHANGE_ATTR, chain);
2880         }
2881         else if (hasChange)
2882         {
2883             return HtmlRendererUtils.renderBehaviorizedAttribute(facesContext,
2884                     writer, HTML.ONCHANGE_ATTR, uiComponent,
2885                     ClientBehaviorEvents.CHANGE, clientBehaviors, HTML.ONCHANGE_ATTR);
2886         }
2887         else if (hasValueChange)
2888         {
2889             return HtmlRendererUtils.renderBehaviorizedAttribute(facesContext,
2890                     writer, HTML.ONCHANGE_ATTR, uiComponent,
2891                     ClientBehaviorEvents.VALUECHANGE, clientBehaviors, HTML.ONCHANGE_ATTR);
2892         }
2893         else
2894         {
2895             return HtmlRendererUtils.renderHTMLStringAttribute(writer, uiComponent,
2896                     HTML.ONCHANGE_ATTR, HTML.ONCHANGE_ATTR);
2897         }
2898     }
2899 
2900     public static boolean renderBehaviorizedOnchangeEventHandler(
2901             FacesContext facesContext, ResponseWriter writer,
2902             UIComponent uiComponent, String targetClientId,
2903             Map<String, List<ClientBehavior>> clientBehaviors)
2904             throws IOException
2905     {
2906         boolean hasChange = HtmlRendererUtils.hasClientBehavior(
2907                 ClientBehaviorEvents.CHANGE, clientBehaviors, facesContext);
2908         boolean hasValueChange = HtmlRendererUtils
2909                 .hasClientBehavior(ClientBehaviorEvents.VALUECHANGE,
2910                         clientBehaviors, facesContext);
2911 
2912         if (hasChange && hasValueChange)
2913         {
2914             String chain = HtmlRendererUtils.buildBehaviorChain(facesContext,
2915                     uiComponent, targetClientId, ClientBehaviorEvents.CHANGE,
2916                     null, ClientBehaviorEvents.VALUECHANGE, null,
2917                     clientBehaviors,
2918                     (String) uiComponent.getAttributes().get(HTML.ONCHANGE_ATTR), null);
2919 
2920             return HtmlRendererUtils.renderHTMLStringAttribute(writer,
2921                     HTML.ONCHANGE_ATTR, HTML.ONCHANGE_ATTR, chain);
2922         }
2923         else if (hasChange)
2924         {
2925             return HtmlRendererUtils.renderBehaviorizedAttribute(facesContext,
2926                     writer, HTML.ONCHANGE_ATTR, uiComponent, targetClientId,
2927                     ClientBehaviorEvents.CHANGE, clientBehaviors, HTML.ONCHANGE_ATTR);
2928         }
2929         else if (hasValueChange)
2930         {
2931             return HtmlRendererUtils.renderBehaviorizedAttribute(facesContext,
2932                     writer, HTML.ONCHANGE_ATTR, uiComponent, targetClientId,
2933                     ClientBehaviorEvents.VALUECHANGE, clientBehaviors, HTML.ONCHANGE_ATTR);
2934         }
2935         else
2936         {
2937             return HtmlRendererUtils.renderHTMLStringAttribute(writer, uiComponent,
2938                     HTML.ONCHANGE_ATTR, HTML.ONCHANGE_ATTR);
2939         }
2940     }
2941 
2942     public static void renderViewStateJavascript(FacesContext facesContext,
2943             String hiddenId, String serializedState) throws IOException
2944     {
2945         HtmlJavaScriptUtils.renderViewStateJavascript(facesContext, hiddenId, serializedState);
2946     }
2947 
2948     /**
2949      * Returns the value of the hideNoSelectionOption attribute of the given UIComponent
2950      * @param component
2951      * @return
2952      */
2953     public static boolean isHideNoSelectionOption(UIComponent component)
2954     {
2955         // check hideNoSelectionOption for literal value (String) or ValueExpression (Boolean)
2956         Object hideNoSelectionOptionAttr = component.getAttributes().get(
2957                 JSFAttr.HIDE_NO_SELECTION_OPTION_ATTR);
2958         return ((hideNoSelectionOptionAttr instanceof String && "true"
2959                 .equalsIgnoreCase((String) hideNoSelectionOptionAttr)) || 
2960                 (hideNoSelectionOptionAttr instanceof Boolean && ((Boolean) hideNoSelectionOptionAttr)));
2961     }
2962 
2963     /**
2964      * Renders all FacesMessages which have not been rendered yet with
2965      * the help of a HtmlMessages component.
2966      * @param facesContext
2967      */
2968     public static void renderUnhandledFacesMessages(FacesContext facesContext)
2969             throws IOException
2970     {
2971         // create and configure HtmlMessages component
2972         HtmlMessages messages = (HtmlMessages) facesContext.getApplication()
2973                 .createComponent(HtmlMessages.COMPONENT_TYPE);
2974         messages.setId("javax_faces_developmentstage_messages");
2975         messages.setTitle("Project Stage[Development]: Unhandled Messages");
2976         messages.setStyle("color:orange");
2977         messages.setRedisplay(false);
2978         // render the component
2979         messages.encodeAll(facesContext);
2980     }
2981 
2982     /**
2983      * The ScriptContext offers methods and fields
2984      * to help with rendering out a script and keeping a
2985      * proper formatting.
2986      */
2987     public static class ScriptContext extends JavascriptContext
2988     {
2989         public ScriptContext()
2990         {
2991             super();
2992         }
2993         public ScriptContext(boolean prettyPrint)
2994         {
2995             super(prettyPrint);
2996         }
2997         public ScriptContext(StringBuilder buf, boolean prettyPrint)
2998         {
2999             super(buf, prettyPrint);
3000         }
3001     }
3002 }