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