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: 1341400 $ $Date: 2012-05-22 05:57:55 -0500 (Tue, 22 May 2012) $
81   */
82  public final class HtmlRendererUtils
83  {
84      //private static final Log log = LogFactory.getLog(HtmlRendererUtils.class);
85      private static final Logger log = Logger.getLogger(HtmlRendererUtils.class
86              .getName());
87      //private static final String[] EMPTY_STRING_ARRAY = new String[0];
88      private static final String LINE_SEPARATOR = System.getProperty(
89              "line.separator", "\r\n");
90      private static final char TABULATOR = '\t';
91      public static final String HIDDEN_COMMANDLINK_FIELD_NAME = "_idcl";
92      public static final String HIDDEN_COMMANDLINK_FIELD_NAME_MYFACES_OLD = "_link_hidden_";
93      public static final String HIDDEN_COMMANDLINK_FIELD_NAME_TRINIDAD = "source";
94      public static final String CLEAR_HIDDEN_FIELD_FN_NAME = "clearFormHiddenParams";
95      public static final String SUBMIT_FORM_FN_NAME = "oamSubmitForm";
96      public static final String SUBMIT_FORM_FN_NAME_JSF2 = "myfaces.oam.submitForm";
97      public static final String ALLOW_CDATA_SECTION_ON = "org.apache.myfaces.ResponseWriter.CdataSectionOn";
98      public static final String NON_SUBMITTED_VALUE_WARNING 
99              = "There should always be a submitted value for an input if it is rendered,"
100             + " its form is submitted, and it was not originally rendered disabled or read-only."
101             + "  You cannot submit a form after disabling an input element via javascript."
102             + "  Consider setting read-only to true instead"
103             + " or resetting the disabled value back to false prior to form submission.";
104     public static final String STR_EMPTY = "";
105 
106     private HtmlRendererUtils()
107     {
108         // utility class, do not instantiate
109     }
110 
111     /**
112      * Utility to set the submitted value of the provided component from the
113      * data in the current request object.
114      * <p/>
115      * Param component is required to be an EditableValueHolder. On return
116      * from this method, the component's submittedValue property will be
117      * set if the submitted form contained that component.
118      */
119     public static void decodeUIInput(FacesContext facesContext, UIComponent component)
120     {
121         if (!(component instanceof EditableValueHolder))
122         {
123             throw new IllegalArgumentException("Component "
124                     + component.getClientId(facesContext)
125                     + " is not an EditableValueHolder");
126         }
127         Map paramMap = facesContext.getExternalContext()
128                 .getRequestParameterMap();
129         String clientId = component.getClientId(facesContext);
130         if (isDisabledOrReadOnly(component))
131         {
132             return;
133         }
134         if (paramMap.containsKey(clientId))
135         {
136             ((EditableValueHolder) component).setSubmittedValue(paramMap
137                     .get(clientId));
138         }
139         else
140         {
141             log.warning(NON_SUBMITTED_VALUE_WARNING + " Component : "
142                     + RendererUtils.getPathToComponent(component));
143         }
144     }
145 
146     /**
147      * X-CHECKED: tlddoc h:selectBooleanCheckbox
148      *
149      * @param facesContext
150      * @param component
151      */
152     public static void decodeUISelectBoolean(FacesContext facesContext, UIComponent component)
153     {
154         if (!(component instanceof EditableValueHolder))
155         {
156             throw new IllegalArgumentException("Component "
157                     + component.getClientId(facesContext)
158                     + " is not an EditableValueHolder");
159         }
160         if (isDisabledOrReadOnly(component))
161         {
162             return;
163         }
164         Map paramMap = facesContext.getExternalContext()
165                 .getRequestParameterMap();
166         String clientId = component.getClientId(facesContext);
167         if (paramMap.containsKey(clientId))
168         {
169             String reqValue = (String) paramMap.get(clientId);
170             if ((reqValue.equalsIgnoreCase("on")
171                     || reqValue.equalsIgnoreCase("yes") || reqValue
172                     .equalsIgnoreCase("true")))
173             {
174                 ((EditableValueHolder) component).setSubmittedValue(Boolean.TRUE);
175             }
176             else
177             {
178                 ((EditableValueHolder) component).setSubmittedValue(Boolean.FALSE);
179             }
180         }
181         else
182         {
183             ((EditableValueHolder) component).setSubmittedValue(Boolean.FALSE);
184         }
185     }
186 
187     public static boolean isDisabledOrReadOnly(UIComponent component)
188     {
189         return isDisplayValueOnly(component) || isDisabled(component) || isReadOnly(component);
190     }
191 
192     public static boolean isDisabled(UIComponent component)
193     {
194         return isTrue(component.getAttributes().get("disabled"));
195     }
196 
197     public static boolean isReadOnly(UIComponent component)
198     {
199         return isTrue(component.getAttributes().get("readonly"));
200     }
201 
202     private static boolean isTrue(Object obj)
203     {
204         if (obj instanceof String)
205         {
206             return new Boolean((String) obj);
207         }
208         if (!(obj instanceof Boolean))
209         {
210             return false;
211         }
212         return ((Boolean) obj).booleanValue();
213     }
214 
215     /**
216      * X-CHECKED: tlddoc h:selectManyListbox
217      *
218      * @param facesContext
219      * @param component
220      */
221     public static void decodeUISelectMany(FacesContext facesContext, UIComponent component)
222     {
223         if (!(component instanceof EditableValueHolder))
224         {
225             throw new IllegalArgumentException("Component "
226                     + component.getClientId(facesContext)
227                     + " is not an EditableValueHolder");
228         }
229         Map paramValuesMap = facesContext.getExternalContext().getRequestParameterValuesMap();
230         String clientId = component.getClientId(facesContext);
231         if (isDisabledOrReadOnly(component))
232         {
233             return;
234         }
235         if (paramValuesMap.containsKey(clientId))
236         {
237             String[] reqValues = (String[]) paramValuesMap.get(clientId);
238             ((EditableValueHolder) component).setSubmittedValue(reqValues);
239         }
240         else
241         {
242             /* request parameter not found, nothing to decode - set submitted value to an empty array
243                as we should get here only if the component is on a submitted form, is rendered
244                and if the component is not readonly or has not been disabled.
245                So in fact, there must be component value at this location, but for listboxes, comboboxes etc.
246                the submitted value is not posted if no item is selected. */
247             ((EditableValueHolder) component).setSubmittedValue(new String[] {});
248         }
249     }
250 
251     /**
252      * X-CHECKED: tlddoc h:selectManyListbox
253      *
254      * @param facesContext
255      * @param component
256      */
257     public static void decodeUISelectOne(FacesContext facesContext, UIComponent component)
258     {
259         if (!(component instanceof EditableValueHolder))
260         {
261             throw new IllegalArgumentException("Component "
262                     + component.getClientId(facesContext)
263                     + " is not an EditableValueHolder");
264         }
265         if (isDisabledOrReadOnly(component))
266         {
267             return;
268         }
269         Map paramMap = facesContext.getExternalContext().getRequestParameterMap();
270         String clientId = component.getClientId(facesContext);
271         if (paramMap.containsKey(clientId))
272         {
273             //request parameter found, set submitted value
274             ((EditableValueHolder) component).setSubmittedValue(paramMap.get(clientId));
275         }
276         else
277         {
278             //see reason for this action at decodeUISelectMany
279             ((EditableValueHolder) component).setSubmittedValue(STR_EMPTY);
280         }
281     }
282 
283     /**
284      * @since 4.0.0
285      */
286     public static void decodeClientBehaviors(FacesContext facesContext, UIComponent component)
287     {
288         if (component instanceof ClientBehaviorHolder)
289         {
290             ClientBehaviorHolder clientBehaviorHolder = (ClientBehaviorHolder) component;
291             Map<String, List<ClientBehavior>> clientBehaviors = clientBehaviorHolder
292                     .getClientBehaviors();
293             if (clientBehaviors != null && !clientBehaviors.isEmpty())
294             {
295                 Map<String, String> paramMap = facesContext
296                         .getExternalContext().getRequestParameterMap();
297                 String behaviorEventName = paramMap
298                         .get("javax.faces.behavior.event");
299                 if (behaviorEventName != null)
300                 {
301                     List<ClientBehavior> clientBehaviorList = clientBehaviors
302                             .get(behaviorEventName);
303                     if (clientBehaviorList != null
304                             && !clientBehaviorList.isEmpty())
305                     {
306                         String clientId = paramMap.get("javax.faces.source");
307                         if (component.getClientId(facesContext).equals(clientId))
308                         {
309                             if (clientBehaviorList instanceof RandomAccess)
310                             {
311                                 for (int i = 0, size = clientBehaviorList.size(); i < size; i++)
312                                 {
313                                     ClientBehavior clientBehavior = clientBehaviorList.get(i);
314                                     clientBehavior.decode(facesContext, component);
315                                 }
316                             } 
317                             else
318                             {
319                                 for (ClientBehavior clientBehavior : clientBehaviorList)
320                                 {
321                                     clientBehavior.decode(facesContext, component);
322                                 }
323                             }
324                         }
325                     }
326                 }
327             }
328         }
329     }
330 
331     public static void renderListbox(FacesContext facesContext,
332             UISelectOne selectOne, boolean disabled, int size,
333             Converter converter) throws IOException
334     {
335         internalRenderSelect(facesContext, selectOne, disabled, size, false, converter);
336     }
337 
338     public static void renderListbox(FacesContext facesContext,
339             UISelectMany selectMany, boolean disabled, int size,
340             Converter converter) throws IOException
341     {
342         internalRenderSelect(facesContext, selectMany, disabled, size, true, converter);
343     }
344 
345     public static void renderMenu(FacesContext facesContext,
346             UISelectOne selectOne, boolean disabled, Converter converter)
347             throws IOException
348     {
349         internalRenderSelect(facesContext, selectOne, disabled, 1, false, converter);
350     }
351 
352     public static void renderMenu(FacesContext facesContext,
353             UISelectMany selectMany, boolean disabled, Converter converter)
354             throws IOException
355     {
356         internalRenderSelect(facesContext, selectMany, disabled, 1, true, converter);
357     }
358 
359     private static void internalRenderSelect(FacesContext facesContext,
360             UIComponent uiComponent, boolean disabled, int size,
361             boolean selectMany, Converter converter) throws IOException
362     {
363         ResponseWriter writer = facesContext.getResponseWriter();
364         writer.startElement(HTML.SELECT_ELEM, uiComponent);
365         if (uiComponent instanceof ClientBehaviorHolder
366                 && JavascriptUtils.isJavascriptAllowed(facesContext.getExternalContext())
367                 && !((ClientBehaviorHolder) uiComponent).getClientBehaviors().isEmpty())
368         {
369             writer.writeAttribute(HTML.ID_ATTR, uiComponent.getClientId(facesContext), null);
370         }
371         else
372         {
373             HtmlRendererUtils.writeIdIfNecessary(writer, uiComponent, facesContext);
374         }
375         writer.writeAttribute(HTML.NAME_ATTR,
376                 uiComponent.getClientId(facesContext), null);
377         List selectItemList;
378         if (selectMany)
379         {
380             writer.writeAttribute(HTML.MULTIPLE_ATTR, HTML.MULTIPLE_ATTR, null);
381             selectItemList = org.apache.myfaces.shared.renderkit.RendererUtils
382                     .getSelectItemList((UISelectMany) uiComponent, facesContext);
383         }
384         else
385         {
386             selectItemList = RendererUtils.getSelectItemList(
387                     (UISelectOne) uiComponent, facesContext);
388         }
389 
390         if (size == Integer.MIN_VALUE)
391         {
392             //No size given (Listbox) --> size is number of select items
393             writer.writeAttribute(HTML.SIZE_ATTR,
394                     Integer.toString(selectItemList.size()), null);
395         }
396         else
397         {
398             writer.writeAttribute(HTML.SIZE_ATTR, Integer.toString(size), null);
399         }
400         Map<String, List<ClientBehavior>> behaviors = null;
401         if (uiComponent instanceof ClientBehaviorHolder
402                 && JavascriptUtils.isJavascriptAllowed(facesContext
403                         .getExternalContext()))
404         {
405             behaviors = ((ClientBehaviorHolder) uiComponent)
406                     .getClientBehaviors();
407             renderBehaviorizedOnchangeEventHandler(facesContext, writer, uiComponent, behaviors);
408             renderBehaviorizedEventHandlers(facesContext, writer, uiComponent, behaviors);
409             renderBehaviorizedFieldEventHandlersWithoutOnchange(facesContext, writer, uiComponent, behaviors);
410             renderHTMLAttributes(
411                     writer,
412                     uiComponent,
413                     HTML.SELECT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED_AND_EVENTS);
414         }
415         else
416         {
417             renderHTMLAttributes(writer, uiComponent,
418                     HTML.SELECT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED);
419         }
420 
421         if (disabled)
422         {
423             writer.writeAttribute(HTML.DISABLED_ATTR, Boolean.TRUE, null);
424         }
425 
426         if (isReadOnly(uiComponent))
427         {
428             writer.writeAttribute(HTML.READONLY_ATTR, HTML.READONLY_ATTR, null);
429         }
430 
431         Set lookupSet = getSubmittedOrSelectedValuesAsSet(selectMany,
432                 uiComponent, facesContext, converter);
433 
434         renderSelectOptions(facesContext, uiComponent, converter, lookupSet,
435                 selectItemList);
436         // bug #970747: force separate end tag
437         writer.writeText(STR_EMPTY, null);
438         writer.endElement(HTML.SELECT_ELEM);
439     }
440 
441     public static Set getSubmittedOrSelectedValuesAsSet(boolean selectMany,
442             UIComponent uiComponent, FacesContext facesContext, Converter converter)
443     {
444         Set lookupSet;
445         if (selectMany)
446         {
447             UISelectMany uiSelectMany = (UISelectMany) uiComponent;
448             lookupSet = RendererUtils.getSubmittedValuesAsSet(facesContext,
449                     uiComponent, converter, uiSelectMany);
450             if (lookupSet == null)
451             {
452                 lookupSet = RendererUtils.getSelectedValuesAsSet(facesContext,
453                         uiComponent, converter, uiSelectMany);
454             }
455         }
456         else
457         {
458             UISelectOne uiSelectOne = (UISelectOne) uiComponent;
459             Object lookup = uiSelectOne.getSubmittedValue();
460             if (lookup == null)
461             {
462                 lookup = uiSelectOne.getValue();
463                 String lookupString = RendererUtils.getConvertedStringValue(
464                         facesContext, uiComponent, converter, lookup);
465                 lookupSet = Collections.singleton(lookupString);
466             }
467             else if (STR_EMPTY.equals(lookup))
468             {
469                 lookupSet = Collections.EMPTY_SET;
470             }
471             else
472             {
473                 lookupSet = Collections.singleton(lookup);
474             }
475         }
476         return lookupSet;
477     }
478 
479     public static Converter findUISelectManyConverterFailsafe(
480             FacesContext facesContext, UIComponent uiComponent)
481     {
482         // invoke with considerValueType = false
483         return findUISelectManyConverterFailsafe(facesContext, uiComponent, false);
484     }
485 
486     public static Converter findUISelectManyConverterFailsafe(
487             FacesContext facesContext, UIComponent uiComponent,
488             boolean considerValueType)
489     {
490         Converter converter;
491         try
492         {
493             converter = RendererUtils.findUISelectManyConverter(facesContext,
494                     (UISelectMany) uiComponent, considerValueType);
495         }
496         catch (FacesException e)
497         {
498             log.log(Level.SEVERE,
499                     "Error finding Converter for component with id "
500                             + uiComponent.getClientId(facesContext), e);
501             converter = null;
502         }
503         return converter;
504     }
505 
506     public static Converter findUIOutputConverterFailSafe(FacesContext facesContext, UIComponent uiComponent)
507     {
508         Converter converter;
509         try
510         {
511             converter = RendererUtils.findUIOutputConverter(facesContext, (UIOutput) uiComponent);
512         }
513         catch (FacesException e)
514         {
515             log.log(Level.SEVERE,
516                     "Error finding Converter for component with id "
517                             + uiComponent.getClientId(facesContext), e);
518             converter = null;
519         }
520         return converter;
521     }
522 
523     /**
524      * Renders the select options for a <code>UIComponent</code> that is
525      * rendered as an HTML select element.
526      *
527      * @param context        the current <code>FacesContext</code>.
528      * @param component      the <code>UIComponent</code> whose options need to be
529      *                       rendered.
530      * @param converter      <code>component</code>'s converter
531      * @param lookupSet      the <code>Set</code> to use to look up selected options
532      * @param selectItemList the <code>List</code> of <code>SelectItem</code> s to be
533      *                       rendered as HTML option elements.
534      * @throws IOException
535      */
536     public static void renderSelectOptions(FacesContext context,
537             UIComponent component, Converter converter, Set lookupSet,
538             List selectItemList) throws IOException
539     {
540         ResponseWriter writer = context.getResponseWriter();
541         // check for the hideNoSelectionOption attribute
542         boolean hideNoSelectionOption = isHideNoSelectionOption(component);
543         boolean componentDisabled = isTrue(component.getAttributes()
544                 .get("disabled"));
545 
546         for (Iterator it = selectItemList.iterator(); it.hasNext();)
547         {
548             SelectItem selectItem = (SelectItem) it.next();
549             if (selectItem instanceof SelectItemGroup)
550             {
551                 writer.startElement(HTML.OPTGROUP_ELEM, component);
552                 writer.writeAttribute(HTML.LABEL_ATTR, selectItem.getLabel(),
553                         null);
554                 SelectItem[] selectItems = ((SelectItemGroup) selectItem)
555                         .getSelectItems();
556                 renderSelectOptions(context, component, converter, lookupSet,
557                         Arrays.asList(selectItems));
558                 writer.endElement(HTML.OPTGROUP_ELEM);
559             }
560             else
561             {
562                 String itemStrValue = org.apache.myfaces.shared.renderkit.RendererUtils
563                         .getConvertedStringValue(context, component, converter,
564                                 selectItem);
565                 boolean selected = lookupSet.contains(itemStrValue); 
566                 //TODO/FIX: we always compare the String vales, better fill lookupSet with Strings 
567                 //only when useSubmittedValue==true, else use the real item value Objects
568 
569                 // IF the hideNoSelectionOption attribute of the component is true
570                 // AND this selectItem is the "no selection option"
571                 // AND there are currently selected items 
572                 // AND this item (the "no selection option") is not selected
573                 // (if there is currently no value on UISelectOne, lookupSet contains "")
574                 if (hideNoSelectionOption && selectItem.isNoSelectionOption()
575                         && lookupSet.size() != 0
576                         && !(lookupSet.size() == 1 && lookupSet.contains(""))
577                         && !selected)
578                 {
579                     // do not render this selectItem
580                     continue;
581                 }
582 
583                 writer.write(TABULATOR);
584                 writer.startElement(HTML.OPTION_ELEM, component);
585                 if (itemStrValue != null)
586                 {
587                     writer.writeAttribute(HTML.VALUE_ATTR, itemStrValue, null);
588                 }
589                 else
590                 {
591                     writer.writeAttribute(HTML.VALUE_ATTR, "", null);
592                 }
593 
594                 if (selected)
595                 {
596                     writer.writeAttribute(HTML.SELECTED_ATTR, HTML.SELECTED_ATTR, null);
597                 }
598 
599                 boolean disabled = selectItem.isDisabled();
600                 if (disabled)
601                 {
602                     writer.writeAttribute(HTML.DISABLED_ATTR, HTML.DISABLED_ATTR, null);
603                 }
604 
605                 String labelClass = null;
606 
607                 if (componentDisabled || disabled)
608                 {
609                     labelClass = (String) component.getAttributes().get(
610                             JSFAttr.DISABLED_CLASS_ATTR);
611                 }
612                 else
613                 {
614                     labelClass = (String) component.getAttributes().get(
615                             JSFAttr.ENABLED_CLASS_ATTR);
616                 }
617                 if (labelClass != null)
618                 {
619                     writer.writeAttribute("class", labelClass, "labelClass");
620                 }
621 
622                 boolean escape;
623                 if (component instanceof EscapeCapable)
624                 {
625                     escape = ((EscapeCapable) component).isEscape();
626 
627                     // Preserve tomahawk semantic. If escape=false
628                     // all items should be non escaped. If escape
629                     // is true check if selectItem.isEscape() is
630                     // true and do it.
631                     // This is done for remain compatibility.
632                     if (escape && selectItem.isEscape())
633                     {
634                         writer.writeText(selectItem.getLabel(), null);
635                     }
636                     else
637                     {
638                         writer.write(selectItem.getLabel());
639                     }
640                 }
641                 else
642                 {
643                     escape = RendererUtils.getBooleanAttribute(component,
644                             JSFAttr.ESCAPE_ATTR, false);
645                     //default is to escape
646                     //In JSF 1.2, when a SelectItem is created by default 
647                     //selectItem.isEscape() returns true (this property
648                     //is not available on JSF 1.1).
649                     //so, if we found a escape property on the component
650                     //set to true, escape every item, but if not
651                     //check if isEscape() = true first.
652                     if (escape || selectItem.isEscape())
653                     {
654                         writer.writeText(selectItem.getLabel(), null);
655                     }
656                     else
657                     {
658                         writer.write(selectItem.getLabel());
659                     }
660                 }
661 
662                 writer.endElement(HTML.OPTION_ELEM);
663             }
664         }
665     }
666 
667     public static void writePrettyLineSeparator(FacesContext facesContext)
668             throws IOException
669     {
670         if (org.apache.myfaces.shared.config.MyfacesConfig.getCurrentInstance(
671                 facesContext.getExternalContext()).isPrettyHtml())
672         {
673             facesContext.getResponseWriter().write(LINE_SEPARATOR);
674         }
675     }
676 
677     public static void writePrettyIndent(FacesContext facesContext)
678             throws IOException
679     {
680         if (org.apache.myfaces.shared.config.MyfacesConfig.getCurrentInstance(
681                 facesContext.getExternalContext()).isPrettyHtml())
682         {
683             facesContext.getResponseWriter().write('\t');
684         }
685     }
686 
687     /**
688      * @return true, if the attribute was written
689      * @throws java.io.IOException
690      */
691     public static boolean renderHTMLAttribute(ResponseWriter writer,
692             String componentProperty, String attrName, Object value)
693             throws IOException
694     {
695         if (!RendererUtils.isDefaultAttributeValue(value))
696         {
697             // render JSF "styleClass" and "itemStyleClass" attributes as "class"
698             String htmlAttrName = attrName.equals(HTML.STYLE_CLASS_ATTR) ? HTML.CLASS_ATTR
699                     : attrName;
700             writer.writeAttribute(htmlAttrName, value, componentProperty);
701             return true;
702         }
703 
704         return false;
705     }
706 
707     /**
708      * @return true, if the attribute was written
709      * @throws java.io.IOException
710      */
711     public static boolean renderHTMLAttribute(ResponseWriter writer,
712             UIComponent component, String componentProperty, String htmlAttrName)
713             throws IOException
714     {
715         Object value = component.getAttributes().get(componentProperty);
716         return renderHTMLAttribute(writer, componentProperty, htmlAttrName,
717                 value);
718     }
719 
720     /**
721      * @return true, if an attribute was written
722      * @throws java.io.IOException
723      */
724     public static boolean renderHTMLAttributes(ResponseWriter writer,
725             UIComponent component, String[] attributes) throws IOException
726     {
727         boolean somethingDone = false;
728         for (int i = 0, len = attributes.length; i < len; i++)
729         {
730             String attrName = attributes[i];
731             if (renderHTMLAttribute(writer, component, attrName, attrName))
732             {
733                 somethingDone = true;
734             }
735         }
736         return somethingDone;
737     }
738 
739     public static boolean renderHTMLAttributeWithOptionalStartElement(
740             ResponseWriter writer, UIComponent component, String elementName,
741             String attrName, Object value, boolean startElementWritten)
742             throws IOException
743     {
744         if (!org.apache.myfaces.shared.renderkit.RendererUtils
745                 .isDefaultAttributeValue(value))
746         {
747             if (!startElementWritten)
748             {
749                 writer.startElement(elementName, component);
750                 startElementWritten = true;
751             }
752             renderHTMLAttribute(writer, attrName, attrName, value);
753         }
754         return startElementWritten;
755     }
756 
757     public static boolean renderHTMLAttributesWithOptionalStartElement(
758             ResponseWriter writer, UIComponent component, String elementName,
759             String[] attributes) throws IOException
760     {
761         boolean startElementWritten = false;
762         for (int i = 0, len = attributes.length; i < len; i++)
763         {
764             String attrName = attributes[i];
765             Object value = component.getAttributes().get(attrName);
766             if (!RendererUtils.isDefaultAttributeValue(value))
767             {
768                 if (!startElementWritten)
769                 {
770                     writer.startElement(elementName, component);
771                     startElementWritten = true;
772                 }
773                 renderHTMLAttribute(writer, attrName, attrName, value);
774             }
775         }
776         return startElementWritten;
777     }
778 
779     public static boolean renderOptionalEndElement(ResponseWriter writer,
780             UIComponent component, String elementName, String[] attributes)
781             throws IOException
782     {
783         boolean endElementNeeded = false;
784         for (int i = 0, len = attributes.length; i < len; i++)
785         {
786             String attrName = attributes[i];
787             Object value = component.getAttributes().get(attrName);
788             if (!RendererUtils.isDefaultAttributeValue(value))
789             {
790                 endElementNeeded = true;
791                 break;
792             }
793         }
794         if (endElementNeeded)
795         {
796             writer.endElement(elementName);
797             return true;
798         }
799 
800         return false;
801     }
802 
803     public static void writeIdIfNecessary(ResponseWriter writer,
804             UIComponent component, FacesContext facesContext)
805             throws IOException
806     {
807         if (component.getId() != null
808                 && !component.getId().startsWith(UIViewRoot.UNIQUE_ID_PREFIX))
809         {
810             writer.writeAttribute(HTML.ID_ATTR, component.getClientId(facesContext), null);
811         }
812     }
813 
814     public static void writeIdAndNameIfNecessary(ResponseWriter writer,
815             UIComponent component, FacesContext facesContext)
816             throws IOException
817     {
818         if (component.getId() != null
819                 && !component.getId().startsWith(UIViewRoot.UNIQUE_ID_PREFIX))
820         {
821             String clientId = component.getClientId(facesContext);
822             writer.writeAttribute(HTML.ID_ATTR, clientId, null);
823             writer.writeAttribute(HTML.NAME_ATTR, clientId, null);
824         }
825     }
826     
827     /**
828      * Renders a html string type attribute. If the value retrieved from the component 
829      * property is "", the attribute is rendered.
830      * 
831      * @param writer
832      * @param component
833      * @param componentProperty
834      * @param htmlAttrName
835      * @return
836      * @throws IOException
837      */
838     public static final boolean renderHTMLStringPreserveEmptyAttribute(ResponseWriter writer,
839             UIComponent component, String componentProperty, String htmlAttrName)
840             throws IOException
841     {
842         String value = (String) component.getAttributes().get(componentProperty);
843         if (!isDefaultStringPreserveEmptyAttributeValue(value))
844         {
845             writer.writeAttribute(htmlAttrName, value, componentProperty);
846             return true;
847         }
848         return false;
849     }
850     
851     /**
852      * Renders a html string type attribute. If the value retrieved from the component 
853      * property is "", the attribute is rendered.
854      * 
855      * @param writer
856      * @param component
857      * @param componentProperty
858      * @param htmlAttrName
859      * @return
860      * @throws IOException
861      */
862     public static final boolean renderHTMLStringPreserveEmptyAttribute(ResponseWriter writer,
863             String componentProperty, String htmlAttrName, String value)
864             throws IOException
865     {
866         if (!isDefaultStringPreserveEmptyAttributeValue(value))
867         {
868             writer.writeAttribute(htmlAttrName, value, componentProperty);
869             return true;
870         }
871         return false;
872     }
873 
874     /**
875      * Check if the value is the default for String type attributes that requires preserve "" as
876      * a valid value.
877      * 
878      * @param value
879      * @return
880      */
881     private static final boolean isDefaultStringPreserveEmptyAttributeValue(String value)
882     {
883         if (value == null)
884         {
885             return true;
886         }
887         else
888         {
889             return false;
890         }
891     }
892 
893     /**
894      * Renders a html string type attribute. If the value retrieved from the component 
895      * property is "" or null, the attribute is not rendered.
896      * 
897      * @param writer
898      * @param component
899      * @param componentProperty
900      * @param htmlAttrName
901      * @return
902      * @throws IOException
903      */
904     public static final boolean renderHTMLStringAttribute(ResponseWriter writer,
905             UIComponent component, String componentProperty, String htmlAttrName)
906             throws IOException
907     {
908         String value = (String) component.getAttributes().get(componentProperty);
909         if (!isDefaultStringAttributeValue(value))
910         {
911             writer.writeAttribute(htmlAttrName, value, componentProperty);
912             return true;
913         }
914         return false;
915     }
916 
917     /**
918      * Renders a html string type attribute. If the value retrieved from the component 
919      * property is "" or null, the attribute is not rendered.
920      * 
921      * @param writer
922      * @param componentProperty
923      * @param htmlAttrName
924      * @param value
925      * @return
926      * @throws IOException
927      */
928     public static final boolean renderHTMLStringAttribute(ResponseWriter writer,
929             String componentProperty, String htmlAttrName, String value)
930             throws IOException
931     {
932         if (!isDefaultStringAttributeValue(value))
933         {
934             writer.writeAttribute(htmlAttrName, value, componentProperty);
935             return true;
936         }
937         return false;
938     }
939     
940     /**
941      * Check if the value is the default for String type attributes (null or "").
942      * 
943      * @param value
944      * @return
945      */
946     private static final boolean isDefaultStringAttributeValue(String value)
947     {
948         if (value == null)
949         {
950             return true;
951         }
952         else if (value.length() == 0)
953         {
954             return true;
955         }
956         else
957         {
958             return false;
959         }
960     }
961     
962     public static boolean renderHTMLStringNoStyleAttributes(ResponseWriter writer,
963             UIComponent component, String[] attributes) throws IOException
964     {
965         boolean somethingDone = false;
966         for (int i = 0, len = attributes.length; i < len; i++)
967         {
968             String attrName = attributes[i];
969             if (renderHTMLStringAttribute(writer, component, attrName, attrName))
970             {
971                 somethingDone = true;
972             }
973         }
974         return somethingDone;
975     }
976 
977     public static void writeIdAndName(ResponseWriter writer, UIComponent component, FacesContext facesContext)
978             throws IOException
979     {
980         String clientId = component.getClientId(facesContext);
981         writer.writeAttribute(HTML.ID_ATTR, clientId, null);
982         writer.writeAttribute(HTML.NAME_ATTR, clientId, null);
983     }
984 
985     public static void renderDisplayValueOnlyForSelects(
986             FacesContext facesContext, UIComponent uiComponent)
987             throws IOException
988     {
989         // invoke renderDisplayValueOnlyForSelects with considerValueType = false
990         renderDisplayValueOnlyForSelects(facesContext, uiComponent, false);
991     }
992 
993     public static void renderDisplayValueOnlyForSelects(FacesContext facesContext, UIComponent uiComponent,
994             boolean considerValueType) throws IOException
995     {
996         ResponseWriter writer = facesContext.getResponseWriter();
997 
998         List selectItemList = null;
999         Converter converter = null;
1000         boolean isSelectOne = false;
1001 
1002         if (uiComponent instanceof UISelectBoolean)
1003         {
1004             converter = findUIOutputConverterFailSafe(facesContext, uiComponent);
1005 
1006             writer.startElement(HTML.SPAN_ELEM, uiComponent);
1007             writeIdIfNecessary(writer, uiComponent, facesContext);
1008             renderDisplayValueOnlyAttributes(uiComponent, writer);
1009             writer.writeText(RendererUtils.getConvertedStringValue(
1010                     facesContext, uiComponent, converter,
1011                     ((UISelectBoolean) uiComponent).getValue()),
1012                     JSFAttr.VALUE_ATTR);
1013             writer.endElement(HTML.SPAN_ELEM);
1014 
1015         }
1016         else
1017         {
1018             if (uiComponent instanceof UISelectMany)
1019             {
1020                 isSelectOne = false;
1021                 selectItemList = RendererUtils.getSelectItemList(
1022                         (UISelectMany) uiComponent, facesContext);
1023                 converter = findUISelectManyConverterFailsafe(facesContext,
1024                         uiComponent, considerValueType);
1025             }
1026             else if (uiComponent instanceof UISelectOne)
1027             {
1028                 isSelectOne = true;
1029                 selectItemList = RendererUtils.getSelectItemList(
1030                         (UISelectOne) uiComponent, facesContext);
1031                 converter = findUIOutputConverterFailSafe(facesContext,
1032                         uiComponent);
1033             }
1034 
1035             writer.startElement(isSelectOne ? HTML.SPAN_ELEM : HTML.UL_ELEM, uiComponent);
1036             writeIdIfNecessary(writer, uiComponent, facesContext);
1037 
1038             renderDisplayValueOnlyAttributes(uiComponent, writer);
1039 
1040             Set lookupSet = getSubmittedOrSelectedValuesAsSet(
1041                     uiComponent instanceof UISelectMany, uiComponent,
1042                     facesContext, converter);
1043 
1044             renderSelectOptionsAsText(facesContext, uiComponent, converter,
1045                     lookupSet, selectItemList, isSelectOne);
1046 
1047             // bug #970747: force separate end tag
1048             writer.writeText(STR_EMPTY, null);
1049             writer.endElement(isSelectOne ? HTML.SPAN_ELEM : HTML.UL_ELEM);
1050         }
1051 
1052     }
1053 
1054     public static void renderDisplayValueOnlyAttributes(
1055             UIComponent uiComponent, ResponseWriter writer) throws IOException
1056     {
1057         if (!(uiComponent instanceof org.apache.myfaces.shared.component.DisplayValueOnlyCapable))
1058         {
1059             log.severe("Wrong type of uiComponent. needs DisplayValueOnlyCapable.");
1060             renderHTMLAttributes(writer, uiComponent, HTML.COMMON_PASSTROUGH_ATTRIBUTES);
1061 
1062             return;
1063         }
1064 
1065         if (getDisplayValueOnlyStyle(uiComponent) != null
1066                 || getDisplayValueOnlyStyleClass(uiComponent) != null)
1067         {
1068             if (getDisplayValueOnlyStyle(uiComponent) != null)
1069             {
1070                 writer.writeAttribute(HTML.STYLE_ATTR, getDisplayValueOnlyStyle(uiComponent), null);
1071             }
1072             else if (uiComponent.getAttributes().get("style") != null)
1073             {
1074                 writer.writeAttribute(HTML.STYLE_ATTR, uiComponent.getAttributes().get("style"), null);
1075             }
1076 
1077             if (getDisplayValueOnlyStyleClass(uiComponent) != null)
1078             {
1079                 writer.writeAttribute(HTML.CLASS_ATTR, getDisplayValueOnlyStyleClass(uiComponent), null);
1080             }
1081             else if (uiComponent.getAttributes().get("styleClass") != null)
1082             {
1083                 writer.writeAttribute(HTML.CLASS_ATTR, uiComponent.getAttributes().get("styleClass"), null);
1084             }
1085 
1086             renderHTMLAttributes(writer, uiComponent, HTML.COMMON_PASSTROUGH_ATTRIBUTES_WITHOUT_STYLE);
1087         }
1088         else
1089         {
1090             renderHTMLAttributes(writer, uiComponent, HTML.COMMON_PASSTROUGH_ATTRIBUTES);
1091         }
1092     }
1093 
1094     private static void renderSelectOptionsAsText(FacesContext context,
1095             UIComponent component, Converter converter, Set lookupSet,
1096             List selectItemList, boolean isSelectOne) throws IOException
1097     {
1098         ResponseWriter writer = context.getResponseWriter();
1099 
1100         for (Iterator it = selectItemList.iterator(); it.hasNext();)
1101         {
1102             SelectItem selectItem = (SelectItem) it.next();
1103 
1104             if (selectItem instanceof SelectItemGroup)
1105             {
1106                 SelectItem[] selectItems = ((SelectItemGroup) selectItem).getSelectItems();
1107                 renderSelectOptionsAsText(context, component, converter,
1108                         lookupSet, Arrays.asList(selectItems), isSelectOne);
1109             }
1110             else
1111             {
1112                 String itemStrValue = RendererUtils.getConvertedStringValue(
1113                         context, component, converter, selectItem);
1114 
1115                 if (lookupSet.contains(itemStrValue))
1116                 {
1117                     //TODO/FIX: we always compare the String vales, better fill lookupSet with Strings 
1118                     //only when useSubmittedValue==true, else use the real item value Objects
1119                     if (!isSelectOne)
1120                     {
1121                         writer.startElement(HTML.LI_ELEM, component);
1122                     }
1123                     writer.writeText(selectItem.getLabel(), null);
1124                     if (!isSelectOne)
1125                     {
1126                         writer.endElement(HTML.LI_ELEM);
1127                     }
1128                     if (isSelectOne)
1129                     {
1130                         //take care of several choices with the same value; use only the first one
1131                         return;
1132                     }
1133                 }
1134             }
1135         }
1136     }
1137 
1138     public static void renderTableCaption(FacesContext context,
1139             ResponseWriter writer, UIComponent component) throws IOException
1140     {
1141         UIComponent captionFacet = component.getFacet("caption");
1142         if (captionFacet == null)
1143         {
1144             return;
1145         }
1146         String captionClass;
1147         String captionStyle;
1148         if (component instanceof HtmlPanelGrid)
1149         {
1150             HtmlPanelGrid panelGrid = (HtmlPanelGrid) component;
1151             captionClass = panelGrid.getCaptionClass();
1152             captionStyle = panelGrid.getCaptionStyle();
1153         }
1154         else if (component instanceof HtmlDataTable)
1155         {
1156             HtmlDataTable dataTable = (HtmlDataTable) component;
1157             captionClass = dataTable.getCaptionClass();
1158             captionStyle = dataTable.getCaptionStyle();
1159         }
1160         else
1161         {
1162             captionClass = (String) component.getAttributes()
1163                     .get(org.apache.myfaces.shared.renderkit.JSFAttr.CAPTION_CLASS_ATTR);
1164             captionStyle = (String) component.getAttributes()
1165                     .get(org.apache.myfaces.shared.renderkit.JSFAttr.CAPTION_STYLE_ATTR);
1166         }
1167         HtmlRendererUtils.writePrettyLineSeparator(context);
1168         writer.startElement(HTML.CAPTION_ELEM, component);
1169         if (captionClass != null)
1170         {
1171             writer.writeAttribute(HTML.CLASS_ATTR, captionClass, null);
1172         }
1173 
1174         if (captionStyle != null)
1175         {
1176             writer.writeAttribute(HTML.STYLE_ATTR, captionStyle, null);
1177         }
1178         //RendererUtils.renderChild(context, captionFacet);
1179         captionFacet.encodeAll(context);
1180         writer.endElement(HTML.CAPTION_ELEM);
1181     }
1182 
1183     public static String getDisplayValueOnlyStyleClass(UIComponent component)
1184     {
1185         if (component instanceof org.apache.myfaces.shared.component.DisplayValueOnlyCapable)
1186         {
1187             if (((org.apache.myfaces.shared.component.DisplayValueOnlyCapable) component)
1188                     .getDisplayValueOnlyStyleClass() != null)
1189             {
1190                 return ((DisplayValueOnlyCapable) component)
1191                         .getDisplayValueOnlyStyleClass();
1192             }
1193             UIComponent parent = component;
1194             while ((parent = parent.getParent()) != null)
1195             {
1196                 if (parent instanceof org.apache.myfaces.shared.component.DisplayValueOnlyCapable
1197                         && ((org.apache.myfaces.shared.component.DisplayValueOnlyCapable) parent)
1198                                 .getDisplayValueOnlyStyleClass() != null)
1199                 {
1200                     return ((org.apache.myfaces.shared.component.DisplayValueOnlyCapable) parent)
1201                             .getDisplayValueOnlyStyleClass();
1202                 }
1203             }
1204         }
1205         return null;
1206     }
1207 
1208     public static String getDisplayValueOnlyStyle(UIComponent component)
1209     {
1210         if (component instanceof DisplayValueOnlyCapable)
1211         {
1212             if (((org.apache.myfaces.shared.component.DisplayValueOnlyCapable) component)
1213                     .getDisplayValueOnlyStyle() != null)
1214             {
1215                 return ((DisplayValueOnlyCapable) component)
1216                         .getDisplayValueOnlyStyle();
1217             }
1218             UIComponent parent = component;
1219             while ((parent = parent.getParent()) != null)
1220             {
1221                 if (parent instanceof org.apache.myfaces.shared.component.DisplayValueOnlyCapable
1222                         && ((DisplayValueOnlyCapable) parent)
1223                                 .getDisplayValueOnlyStyle() != null)
1224                 {
1225                     return ((DisplayValueOnlyCapable) parent)
1226                             .getDisplayValueOnlyStyle();
1227                 }
1228             }
1229         }
1230         return null;
1231     }
1232 
1233     public static boolean isDisplayValueOnly(UIComponent component)
1234     {
1235         if (component instanceof DisplayValueOnlyCapable)
1236         {
1237             if (((DisplayValueOnlyCapable) component).isSetDisplayValueOnly())
1238             {
1239                 return ((DisplayValueOnlyCapable) component).isDisplayValueOnly();
1240             }
1241             UIComponent parent = component;
1242             while ((parent = parent.getParent()) != null)
1243             {
1244                 if (parent instanceof DisplayValueOnlyCapable
1245                         && ((DisplayValueOnlyCapable) parent).isSetDisplayValueOnly())
1246                 {
1247                     return ((org.apache.myfaces.shared.component.DisplayValueOnlyCapable) parent)
1248                             .isDisplayValueOnly();
1249                 }
1250             }
1251         }
1252         return false;
1253     }
1254 
1255     public static void renderDisplayValueOnly(FacesContext facesContext,
1256             UIInput input) throws IOException
1257     {
1258         ResponseWriter writer = facesContext.getResponseWriter();
1259         writer.startElement(org.apache.myfaces.shared.renderkit.html.HTML.SPAN_ELEM, input);
1260         writeIdIfNecessary(writer, input, facesContext);
1261         renderDisplayValueOnlyAttributes(input, writer);
1262         String strValue = RendererUtils.getStringValue(facesContext, input);
1263         writer.write(HTMLEncoder.encode(strValue, true, true));
1264         writer.endElement(HTML.SPAN_ELEM);
1265     }
1266 
1267     public static void appendClearHiddenCommandFormParamsFunctionCall(
1268             StringBuilder buf, String formName)
1269     {
1270         HtmlJavaScriptUtils.appendClearHiddenCommandFormParamsFunctionCall(buf, formName);
1271     }
1272 
1273     @SuppressWarnings("unchecked")
1274     public static void renderFormSubmitScript(FacesContext facesContext)
1275             throws IOException
1276     {
1277         HtmlJavaScriptUtils.renderFormSubmitScript(facesContext);
1278     }
1279 
1280     /**
1281      * Adds the hidden form input value assignment that is necessary for the autoscroll
1282      * feature to an html link or button onclick attribute.
1283      */
1284     public static void appendAutoScrollAssignment(StringBuilder onClickValue,
1285             String formName)
1286     {
1287         HtmlJavaScriptUtils.appendAutoScrollAssignment(onClickValue, formName);
1288     }
1289 
1290     /**
1291      * Adds the hidden form input value assignment that is necessary for the autoscroll
1292      * feature to an html link or button onclick attribute.
1293      */
1294     public static void appendAutoScrollAssignment(FacesContext context,
1295             StringBuilder onClickValue, String formName)
1296     {
1297         HtmlJavaScriptUtils.appendAutoScrollAssignment(context, onClickValue, formName);
1298     }
1299 
1300     /**
1301      * Renders the hidden form input that is necessary for the autoscroll feature.
1302      */
1303     public static void renderAutoScrollHiddenInput(FacesContext facesContext,
1304             ResponseWriter writer) throws IOException
1305     {
1306         HtmlJavaScriptUtils.renderAutoScrollHiddenInput(facesContext, writer);
1307     }
1308 
1309     /**
1310      * Renders the autoscroll javascript function.
1311      */
1312     public static void renderAutoScrollFunction(FacesContext facesContext,
1313             ResponseWriter writer) throws IOException
1314     {
1315         HtmlJavaScriptUtils.renderAutoScrollFunction(facesContext, writer);
1316     }
1317 
1318     public static String getAutoScrollFunction(FacesContext facesContext)
1319     {
1320         return HtmlJavaScriptUtils.getAutoScrollFunction(facesContext);
1321     }
1322 
1323     public static boolean isAllowedCdataSection(FacesContext fc)
1324     {
1325         Boolean value = null;
1326         if (fc != null)
1327         {
1328             value = (Boolean) fc.getExternalContext().getRequestMap().get(ALLOW_CDATA_SECTION_ON);
1329         }
1330         return value != null && ((Boolean) value).booleanValue();
1331     }
1332 
1333     public static void allowCdataSection(FacesContext fc, boolean cdataSectionAllowed)
1334     {
1335         fc.getExternalContext().getRequestMap().put(ALLOW_CDATA_SECTION_ON, Boolean.valueOf(cdataSectionAllowed));
1336     }
1337 
1338     public static class LinkParameter
1339     {
1340         private String _name;
1341 
1342         private Object _value;
1343 
1344         public String getName()
1345         {
1346             return _name;
1347         }
1348 
1349         public void setName(String name)
1350         {
1351             _name = name;
1352         }
1353 
1354         public Object getValue()
1355         {
1356             return _value;
1357         }
1358 
1359         public void setValue(Object value)
1360         {
1361             _value = value;
1362         }
1363     }
1364 
1365     public static void renderHiddenCommandFormParams(ResponseWriter writer,
1366             Set dummyFormParams) throws IOException
1367     {
1368         for (Iterator it = dummyFormParams.iterator(); it.hasNext();)
1369         {
1370             Object name = it.next();
1371             renderHiddenInputField(writer, name, null);
1372         }
1373     }
1374 
1375     public static void renderHiddenInputField(ResponseWriter writer,
1376             Object name, Object value) throws IOException
1377     {
1378         writer.startElement(HTML.INPUT_ELEM, null);
1379         writer.writeAttribute(HTML.TYPE_ATTR, HTML.INPUT_TYPE_HIDDEN, null);
1380         writer.writeAttribute(HTML.NAME_ATTR, name, null);
1381         if (value != null)
1382         {
1383             writer.writeAttribute(HTML.VALUE_ATTR, value, null);
1384         }
1385         writer.endElement(HTML.INPUT_ELEM);
1386     }
1387 
1388     /**
1389      * @deprecated Replaced by
1390      *             renderLabel(ResponseWriter writer,
1391      *             UIComponent component,
1392      *             String forClientId,
1393      *             SelectItem item,
1394      *             boolean disabled).
1395      *             Renders a label HTML element
1396      */
1397     @Deprecated
1398     public static void renderLabel(ResponseWriter writer,
1399             UIComponent component, String forClientId, String labelValue,
1400             boolean disabled) throws IOException
1401     {
1402         writer.startElement(HTML.LABEL_ELEM, component);
1403         writer.writeAttribute(HTML.FOR_ATTR, forClientId, null);
1404         String labelClass = null;
1405         if (disabled)
1406         {
1407             labelClass = (String) component.getAttributes().get(JSFAttr.DISABLED_CLASS_ATTR);
1408         }
1409         else
1410         {
1411             labelClass = (String) component.getAttributes()
1412                     .get(org.apache.myfaces.shared.renderkit.JSFAttr.ENABLED_CLASS_ATTR);
1413         }
1414         if (labelClass != null)
1415         {
1416             writer.writeAttribute("class", labelClass, "labelClass");
1417         }
1418         if ((labelValue != null) && (labelValue.length() > 0))
1419         {
1420             writer.write(HTML.NBSP_ENTITY);
1421             writer.writeText(labelValue, null);
1422         }
1423         writer.endElement(HTML.LABEL_ELEM);
1424     }
1425 
1426     /**
1427      * Renders a label HTML element
1428      */
1429     public static void renderLabel(ResponseWriter writer,
1430             UIComponent component, String forClientId, SelectItem item,
1431             boolean disabled) throws IOException
1432     {
1433         writer.startElement(HTML.LABEL_ELEM, component);
1434         writer.writeAttribute(HTML.FOR_ATTR, forClientId, null);
1435         String labelClass = null;
1436         if (disabled)
1437         {
1438             labelClass = (String) component.getAttributes().get(JSFAttr.DISABLED_CLASS_ATTR);
1439         }
1440         else
1441         {
1442             labelClass = (String) component.getAttributes()
1443                     .get(org.apache.myfaces.shared.renderkit.JSFAttr.ENABLED_CLASS_ATTR);
1444         }
1445         if (labelClass != null)
1446         {
1447             writer.writeAttribute("class", labelClass, "labelClass");
1448         }
1449         if ((item.getLabel() != null) && (item.getLabel().length() > 0))
1450         {
1451             // writer.write(HTML.NBSP_ENTITY);
1452             writer.write(" ");
1453             if (item.isEscape())
1454             {
1455                 //writer.write(item.getLabel());
1456                 writer.writeText(item.getLabel(), null);
1457             }
1458             else
1459             {
1460                 //writer.write(HTMLEncoder.encode (item.getLabel()));
1461                 writer.write(item.getLabel());
1462             }
1463         }
1464         writer.endElement(HTML.LABEL_ELEM);
1465     }
1466 
1467     /**
1468      * Renders a label HTML element
1469      */
1470     public static void renderLabel(ResponseWriter writer,
1471             UIComponent component, String forClientId, SelectItem item,
1472             boolean disabled, boolean selected) throws IOException
1473     {
1474         writer.startElement(HTML.LABEL_ELEM, component);
1475         writer.writeAttribute(HTML.FOR_ATTR, forClientId, null);
1476         String labelClass = null;
1477         if (disabled)
1478         {
1479             labelClass = (String) component.getAttributes().get(JSFAttr.DISABLED_CLASS_ATTR);
1480         }
1481         else
1482         {
1483             labelClass = (String) component.getAttributes()
1484                     .get(org.apache.myfaces.shared.renderkit.JSFAttr.ENABLED_CLASS_ATTR);
1485         }
1486         String labelSelectedClass = null;
1487         if (selected)
1488         {
1489             labelSelectedClass = (String) component.getAttributes().get(JSFAttr.SELECTED_CLASS_ATTR);
1490         }
1491         else
1492         {
1493             labelSelectedClass = (String) component.getAttributes().get(JSFAttr.UNSELECTED_CLASS_ATTR);
1494         }
1495         if (labelSelectedClass != null)
1496         {
1497             if (labelClass == null)
1498             {
1499                 labelClass = labelSelectedClass;
1500             }
1501             else
1502             {
1503                 labelClass = labelClass + " " + labelSelectedClass;
1504             }
1505         }
1506         if (labelClass != null)
1507         {
1508             writer.writeAttribute("class", labelClass, "labelClass");
1509         }
1510         if ((item.getLabel() != null) && (item.getLabel().length() > 0))
1511         {
1512             writer.write(HTML.NBSP_ENTITY);
1513             if (item.isEscape())
1514             {
1515                 //writer.write(item.getLabel());
1516                 writer.writeText(item.getLabel(), null);
1517             }
1518             else
1519             {
1520                 //writer.write(HTMLEncoder.encode (item.getLabel()));
1521                 writer.write(item.getLabel());
1522             }
1523         }
1524         writer.endElement(HTML.LABEL_ELEM);
1525     }
1526 
1527     /**
1528      * Render the javascript function that is called on a click on a commandLink
1529      * to clear the hidden inputs. This is necessary because on a browser back,
1530      * each hidden input still has it's old value (browser cache!) and therefore
1531      * a new submit would cause the according action once more!
1532      *
1533      * @param writer
1534      * @param formName
1535      * @param dummyFormParams
1536      * @param formTarget
1537      * @throws IOException
1538      */
1539     public static void renderClearHiddenCommandFormParamsFunction(
1540             ResponseWriter writer, String formName, Set dummyFormParams,
1541             String formTarget) throws IOException
1542     {
1543         HtmlJavaScriptUtils.renderClearHiddenCommandFormParamsFunction(writer, formName, dummyFormParams, formTarget);
1544     }
1545 
1546     /**
1547      * Prefixes the given String with "clear_" and removes special characters
1548      *
1549      * @param formName
1550      * @return String
1551      */
1552     public static String getClearHiddenCommandFormParamsFunctionName(
1553             String formName)
1554     {
1555         return HtmlJavaScriptUtils.getClearHiddenCommandFormParamsFunctionName(formName);
1556     }
1557 
1558     public static String getClearHiddenCommandFormParamsFunctionNameMyfacesLegacy(
1559             String formName)
1560     {
1561         return HtmlJavaScriptUtils.getClearHiddenCommandFormParamsFunctionNameMyfacesLegacy(formName);
1562     }
1563 
1564     /**
1565      * Get the name of the request parameter that holds the id of the
1566      * link-type component that caused the form to be submitted.
1567      * <p/>
1568      * Within each page there may be multiple "link" type components that
1569      * cause page submission. On the server it is necessary to know which
1570      * of these actually caused the submit, in order to invoke the correct
1571      * listeners. Such components therefore store their id into the
1572      * "hidden command link field" in their associated form before
1573      * submitting it.
1574      * <p/>
1575      * The field is always a direct child of each form, and has the same
1576      * <i>name</i> in each form. The id of the form component is therefore
1577      * both necessary and sufficient to determine the full name of the
1578      * field.
1579      */
1580     public static String getHiddenCommandLinkFieldName(FormInfo formInfo)
1581     {
1582         if (RendererUtils.isAdfOrTrinidadForm(formInfo.getForm()))
1583         {
1584             return HIDDEN_COMMANDLINK_FIELD_NAME_TRINIDAD;
1585         }
1586         return formInfo.getFormName() + UINamingContainer.getSeparatorChar(FacesContext
1587                         .getCurrentInstance()) + HIDDEN_COMMANDLINK_FIELD_NAME;
1588     }
1589     
1590     public static String getHiddenCommandLinkFieldName(
1591             FormInfo formInfo, FacesContext facesContext)
1592     {
1593         if (RendererUtils.isAdfOrTrinidadForm(formInfo.getForm()))
1594         {
1595             return HIDDEN_COMMANDLINK_FIELD_NAME_TRINIDAD;
1596         }
1597         return formInfo.getFormName() + UINamingContainer.getSeparatorChar(facesContext)
1598                 + HIDDEN_COMMANDLINK_FIELD_NAME;
1599     }
1600 
1601     public static boolean isPartialOrBehaviorSubmit(FacesContext facesContext,
1602             String clientId)
1603     {
1604         Map<String, String> params = facesContext.getExternalContext().getRequestParameterMap();
1605         String sourceId = params.get("javax.faces.source");
1606         if (sourceId == null || !sourceId.equals(clientId))
1607         {
1608             return false;
1609         }
1610         boolean partialOrBehaviorSubmit = false;
1611         String behaviorEvent = params.get("javax.faces.behavior.event");
1612         if (behaviorEvent != null)
1613         {
1614             partialOrBehaviorSubmit = ClientBehaviorEvents.ACTION.equals(behaviorEvent);
1615             if (partialOrBehaviorSubmit)
1616             {
1617                 return partialOrBehaviorSubmit;
1618             }
1619         }
1620         String partialEvent = params.get("javax.faces.partial.event");
1621         if (partialEvent != null)
1622         {
1623             partialOrBehaviorSubmit = ClientBehaviorEvents.CLICK.equals(partialEvent);
1624         }
1625         return partialOrBehaviorSubmit;
1626     }
1627 
1628     public static String getHiddenCommandLinkFieldNameMyfacesOld(
1629             FormInfo formInfo)
1630     {
1631         return formInfo.getFormName() + UINamingContainer.getSeparatorChar(FacesContext.getCurrentInstance())
1632                 + HIDDEN_COMMANDLINK_FIELD_NAME_MYFACES_OLD;
1633     }
1634 
1635     public static String getOutcomeTargetHref(FacesContext facesContext,
1636             UIOutcomeTarget component) throws IOException
1637     {
1638         String outcome = component.getOutcome();
1639         outcome = (outcome == null) ? facesContext.getViewRoot().getViewId()
1640                 : outcome;
1641         outcome = ((outcome == null) ? STR_EMPTY : outcome.trim());
1642         // Get the correct URL for the outcome.
1643         NavigationHandler nh = facesContext.getApplication().getNavigationHandler();
1644         if (!(nh instanceof ConfigurableNavigationHandler))
1645         {
1646             throw new FacesException(
1647                     "Navigation handler must be an instance of "
1648                             + "ConfigurableNavigationHandler for using h:link or h:button");
1649         }
1650         ConfigurableNavigationHandler navigationHandler = (ConfigurableNavigationHandler) nh;
1651         // fromAction is null because there is no action method that was called to get the outcome
1652         NavigationCase navigationCase = navigationHandler.getNavigationCase(
1653                 facesContext, null, outcome);
1654         // when navigation case is null, force the link or button to be disabled and log a warning
1655         if (navigationCase == null)
1656         {
1657             // log a warning
1658             log.warning("Could not determine NavigationCase for UIOutcomeTarget component "
1659                     + RendererUtils.getPathToComponent(component));
1660 
1661             return null;
1662         }
1663         Map<String, List<String>> parameters = null;
1664         // handle URL parameters
1665         if (component.getChildCount() > 0)
1666         {
1667             List<UIParameter> validParams = getValidUIParameterChildren(
1668                     facesContext, component.getChildren(), true, false);
1669             if (validParams.size() > 0)
1670             {
1671                 parameters = new HashMap<String, List<String>>();
1672             }
1673             for (int i = 0, size = validParams.size(); i < size; i++)
1674             {
1675                 UIParameter param = validParams.get(i);
1676                 String name = param.getName();
1677                 Object value = param.getValue();
1678                 if (parameters.containsKey(name))
1679                 {
1680                     parameters.get(name).add(value.toString());
1681                 }
1682                 else
1683                 {
1684                     List<String> list = new ArrayList<String>(1);
1685                     list.add(value.toString());
1686                     parameters.put(name, list);
1687                 }
1688             }
1689         }
1690         // handle NavigationCase parameters
1691         Map<String, List<String>> navigationCaseParams = 
1692             NavigationUtils.getEvaluatedNavigationParameters(facesContext,
1693                 navigationCase.getParameters());
1694         if (navigationCaseParams != null)
1695         {
1696             if (parameters == null)
1697             {
1698                 parameters = new HashMap<String, List<String>>();
1699             }
1700             //parameters.putAll(navigationCaseParams);
1701             for (Map.Entry<String, List<String>> entry : navigationCaseParams
1702                     .entrySet())
1703             {
1704                 if (!parameters.containsKey(entry.getKey()))
1705                 {
1706                     parameters.put(entry.getKey(), entry.getValue());
1707                 }
1708             }
1709         }
1710         if (parameters == null)
1711         {
1712             parameters = Collections.emptyMap();
1713         }
1714         // In theory the precedence order to deal with params is this:
1715         // component parameters, navigation-case parameters, view parameters
1716         // getBookmarkableURL deal with this details.
1717         ViewHandler viewHandler = facesContext.getApplication().getViewHandler();
1718         String href = viewHandler.getBookmarkableURL(facesContext,
1719                 navigationCase.getToViewId(facesContext),
1720                 parameters, navigationCase.isIncludeViewParams() || component.isIncludeViewParams());
1721         // handle fragment (viewId#fragment)
1722         String fragment = (String) component.getAttributes().get("fragment");
1723         if (fragment != null)
1724         {
1725             fragment = fragment.trim();
1726 
1727             if (fragment.length() > 0)
1728             {
1729                 href += "#" + fragment;
1730             }
1731         }
1732         return href;
1733     }
1734 
1735     private static final String HTML_CONTENT_TYPE = "text/html";
1736     private static final String TEXT_ANY_CONTENT_TYPE = "text/*";
1737     private static final String ANY_CONTENT_TYPE = "*/*";
1738     public static final String DEFAULT_CHAR_ENCODING = "ISO-8859-1";
1739     private static final String XHTML_CONTENT_TYPE = "application/xhtml+xml";
1740     private static final String APPLICATION_XML_CONTENT_TYPE = "application/xml";
1741     private static final String TEXT_XML_CONTENT_TYPE = "text/xml";
1742     // The order is important in this case.
1743     private static final String[] SUPPORTED_CONTENT_TYPES = {
1744             HTML_CONTENT_TYPE, //Prefer this over any other, because IE does not support XHTML content type
1745             XHTML_CONTENT_TYPE, APPLICATION_XML_CONTENT_TYPE,
1746             TEXT_XML_CONTENT_TYPE, TEXT_ANY_CONTENT_TYPE, ANY_CONTENT_TYPE };
1747     /**
1748      * @deprecated use ContentTypeUtils instead
1749      */
1750     @Deprecated
1751     public static String selectContentType(String contentTypeListString)
1752     {
1753         if (contentTypeListString == null)
1754         {
1755             FacesContext context = FacesContext.getCurrentInstance();
1756             if (context != null)
1757             {
1758                 contentTypeListString = (String) context.getExternalContext()
1759                         .getRequestHeaderMap().get("Accept");
1760                 // There is a windows mobile IE client (6.12) sending
1761                 // "application/vnd.wap.mms-message;*/*"
1762                 // Note that the Accept header should be written as 
1763                 // "application/vnd.wap.mms-message,*/*" ,
1764                 // so this is bug of the client. Anyway, this is a workaround ...
1765                 if (contentTypeListString != null
1766                         && contentTypeListString.startsWith("application/vnd.wap.mms-message;*/*"))
1767                 {
1768                     contentTypeListString = "*/*";
1769                 }
1770             }
1771             if (contentTypeListString == null)
1772             {
1773                 if (log.isLoggable(Level.FINE))
1774                 {
1775                     log.fine("No content type list given, creating HtmlResponseWriterImpl with default content type.");
1776                 }
1777                 contentTypeListString = HTML_CONTENT_TYPE;
1778             }
1779         }
1780         List contentTypeList = splitContentTypeListString(contentTypeListString);
1781         String[] supportedContentTypeArray = getSupportedContentTypes();
1782         String selectedContentType = null;
1783         for (int i = 0; i < supportedContentTypeArray.length; i++)
1784         {
1785             String supportedContentType = supportedContentTypeArray[i].trim();
1786 
1787             for (int j = 0; j < contentTypeList.size(); j++)
1788             {
1789                 String contentType = (String) contentTypeList.get(j);
1790 
1791                 if (contentType.indexOf(supportedContentType) != -1)
1792                 {
1793                     if (isHTMLContentType(contentType))
1794                     {
1795                         selectedContentType = HTML_CONTENT_TYPE;
1796                     }
1797                     else if (isXHTMLContentType(contentType))
1798                     {
1799                         selectedContentType = XHTML_CONTENT_TYPE;
1800                     }
1801                     break;
1802                 }
1803             }
1804             if (selectedContentType != null)
1805             {
1806                 break;
1807             }
1808         }
1809         if (selectedContentType == null)
1810         {
1811             throw new IllegalArgumentException(
1812                     "ContentTypeList does not contain a supported content type: "
1813                             + contentTypeListString);
1814         }
1815         return selectedContentType;
1816     }
1817 
1818     public static String[] getSupportedContentTypes()
1819     {
1820         //String[] supportedContentTypeArray = new String[]{
1821         // HTML_CONTENT_TYPE,TEXT_ANY_CONTENT_TYPE,ANY_CONTENT_TYPE,
1822         // XHTML_CONTENT_TYPE,APPLICATION_XML_CONTENT_TYPE,TEXT_XML_CONTENT_TYPE};
1823         return SUPPORTED_CONTENT_TYPES;
1824     }
1825 
1826     private static boolean isHTMLContentType(String contentType)
1827     {
1828         return contentType.indexOf(HTML_CONTENT_TYPE) != -1
1829                 || contentType.indexOf(ANY_CONTENT_TYPE) != -1
1830                 || contentType.indexOf(TEXT_ANY_CONTENT_TYPE) != -1;
1831     }
1832 
1833     public static boolean isXHTMLContentType(String contentType)
1834     {
1835         return contentType.indexOf(XHTML_CONTENT_TYPE) != -1
1836                 || contentType.indexOf(APPLICATION_XML_CONTENT_TYPE) != -1
1837                 || contentType.indexOf(TEXT_XML_CONTENT_TYPE) != -1;
1838     }
1839 
1840     private static List splitContentTypeListString(String contentTypeListString)
1841     {
1842         List contentTypeList = new ArrayList();
1843         StringTokenizer st = new StringTokenizer(contentTypeListString, ",");
1844         while (st.hasMoreTokens())
1845         {
1846             String contentType = st.nextToken().trim();
1847             int semicolonIndex = contentType.indexOf(";");
1848             if (semicolonIndex != -1)
1849             {
1850                 contentType = contentType.substring(0, semicolonIndex);
1851             }
1852             contentTypeList.add(contentType);
1853         }
1854         return contentTypeList;
1855     }
1856 
1857     public static String getJavascriptLocation(UIComponent component)
1858     {
1859         if (component == null)
1860         {
1861             return null;
1862         }
1863         return (String) component.getAttributes().get(JSFAttr.JAVASCRIPT_LOCATION);
1864     }
1865 
1866     public static String getImageLocation(UIComponent component)
1867     {
1868         if (component == null)
1869         {
1870             return null;
1871         }
1872         return (String) component.getAttributes().get(JSFAttr.IMAGE_LOCATION);
1873     }
1874 
1875     public static String getStyleLocation(UIComponent component)
1876     {
1877         if (component == null)
1878         {
1879             return null;
1880         }
1881         return (String) component.getAttributes().get(JSFAttr.STYLE_LOCATION);
1882     }
1883 
1884     /**
1885      * Checks if the given component has a behavior attachment with a given name.
1886      *
1887      * @param eventName the event name to be checked for
1888      * @param behaviors map of behaviors attached to the component
1889      * @return true if client behavior with given name is attached, false otherwise
1890      * @since 4.0.0
1891      */
1892     public static boolean hasClientBehavior(String eventName,
1893             Map<String, List<ClientBehavior>> behaviors,
1894             FacesContext facesContext)
1895     {
1896         if (behaviors == null)
1897         {
1898             return false;
1899         }
1900         return (behaviors.get(eventName) != null);
1901     }
1902 
1903     public static Collection<ClientBehaviorContext.Parameter> getClientBehaviorContextParameters(
1904             Map<String, String> params)
1905     {
1906         List<ClientBehaviorContext.Parameter> paramList = null;
1907         if (params != null)
1908         {
1909             paramList = new ArrayList<ClientBehaviorContext.Parameter>(params.size());
1910             for (Map.Entry<String, String> paramEntry : params.entrySet())
1911             {
1912                 paramList.add(new ClientBehaviorContext.Parameter(paramEntry
1913                         .getKey(), paramEntry.getValue()));
1914             }
1915         }
1916         return paramList;
1917     }
1918 
1919     /**
1920      * builds the chained behavior script which then can be reused
1921      * in following order by the other script building parts
1922      * <p/>
1923      * user defined event handling script
1924      * behavior script
1925      * renderer default script
1926      *
1927      * @param eventName    event name ("onclick" etc...)
1928      * @param uiComponent  the component which has the attachement (or should have)
1929      * @param facesContext the facesContext
1930      * @param params       params map of params which have to be dragged into the request
1931      * @return a string representation of the javascripts for the attached event behavior,
1932      *         an empty string if none is present
1933      * @since 4.0.0
1934      */
1935     private static boolean getClientBehaviorScript(FacesContext facesContext,
1936             UIComponent uiComponent, String eventName,
1937             Map<String, List<ClientBehavior>> clientBehaviors,
1938             ScriptContext target,
1939             Collection<ClientBehaviorContext.Parameter> params)
1940     {
1941         return getClientBehaviorScript(facesContext, uiComponent,
1942                 uiComponent.getClientId(facesContext), eventName,
1943                 clientBehaviors, target, params);
1944     }
1945 
1946     private static boolean getClientBehaviorScript(FacesContext facesContext,
1947             UIComponent uiComponent, String targetClientId, String eventName,
1948             Map<String, List<ClientBehavior>> clientBehaviors,
1949             ScriptContext target,
1950             Collection<ClientBehaviorContext.Parameter> params)
1951     {
1952         if (!(uiComponent instanceof ClientBehaviorHolder))
1953         {
1954             target.append(STR_EMPTY);
1955             return false;
1956         }
1957         ExternalContext externalContext = facesContext.getExternalContext();
1958         boolean renderClientBehavior = JavascriptUtils
1959                 .isJavascriptAllowed(externalContext)
1960                 && clientBehaviors != null && clientBehaviors.size() > 0;
1961         if (!renderClientBehavior)
1962         {
1963             target.append(STR_EMPTY);
1964             return false;
1965         }
1966         List<ClientBehavior> attachedEventBehaviors = clientBehaviors
1967                 .get(eventName);
1968         if (attachedEventBehaviors == null
1969                 || attachedEventBehaviors.size() == 0)
1970         {
1971             target.append(STR_EMPTY);
1972             return false;
1973         }
1974         ClientBehaviorContext context = ClientBehaviorContext
1975                 .createClientBehaviorContext(facesContext, uiComponent,
1976                         eventName, targetClientId, params);
1977         boolean submitting = false;
1978         
1979         // List<ClientBehavior>  attachedEventBehaviors is  99% _DeltaList created in
1980         // javax.faces.component.UIComponentBase.addClientBehavior
1981         if (attachedEventBehaviors instanceof RandomAccess)
1982         {
1983             for (int i = 0, size = attachedEventBehaviors.size(); i < size; i++)
1984             {
1985                 ClientBehavior clientBehavior = attachedEventBehaviors.get(i);
1986                 submitting =  _appendClientBehaviourScript(target, context, 
1987                         submitting, i < (size -1), clientBehavior);   
1988             }
1989         }
1990         else 
1991         {
1992             Iterator<ClientBehavior> clientIterator = attachedEventBehaviors.iterator();
1993             while (clientIterator.hasNext())
1994             {
1995                 ClientBehavior clientBehavior = clientIterator.next();
1996                 submitting = _appendClientBehaviourScript(target, context, submitting, 
1997                         clientIterator.hasNext(), clientBehavior);
1998             }
1999         }
2000         
2001         return submitting;
2002     }
2003 
2004     private static boolean _appendClientBehaviourScript(ScriptContext target, ClientBehaviorContext context, 
2005             boolean submitting, boolean hasNext, ClientBehavior clientBehavior)
2006     {
2007         String script = clientBehavior.getScript(context);
2008         // The script _can_ be null, and in fact is for <f:ajax disabled="true" />
2009         if (script != null)
2010         {
2011             //either strings or functions, but I assume string is more appropriate 
2012             //since it allows access to the
2013             //origin as this!
2014             target.append("'" + escapeJavaScriptForChain(script) + "'");
2015             if (hasNext)
2016             {
2017                 target.append(", ");
2018             }
2019         }
2020         if (!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 
2910         if (hasChange && hasValueChange)
2911         {
2912             String chain = HtmlRendererUtils.buildBehaviorChain(facesContext,
2913                     uiComponent, targetClientId, ClientBehaviorEvents.CHANGE,
2914                     null, ClientBehaviorEvents.VALUECHANGE, null,
2915                     clientBehaviors,
2916                     (String) uiComponent.getAttributes().get(HTML.ONCHANGE_ATTR), null);
2917 
2918             return HtmlRendererUtils.renderHTMLStringAttribute(writer,
2919                     HTML.ONCHANGE_ATTR, HTML.ONCHANGE_ATTR, chain);
2920         }
2921         else if (hasChange)
2922         {
2923             return HtmlRendererUtils.renderBehaviorizedAttribute(facesContext,
2924                     writer, HTML.ONCHANGE_ATTR, uiComponent, targetClientId,
2925                     ClientBehaviorEvents.CHANGE, clientBehaviors, HTML.ONCHANGE_ATTR);
2926         }
2927         else if (hasValueChange)
2928         {
2929             return HtmlRendererUtils.renderBehaviorizedAttribute(facesContext,
2930                     writer, HTML.ONCHANGE_ATTR, uiComponent, targetClientId,
2931                     ClientBehaviorEvents.VALUECHANGE, clientBehaviors, HTML.ONCHANGE_ATTR);
2932         }
2933         else
2934         {
2935             return HtmlRendererUtils.renderHTMLStringAttribute(writer, uiComponent,
2936                     HTML.ONCHANGE_ATTR, HTML.ONCHANGE_ATTR);
2937         }
2938     }
2939 
2940     public static void renderViewStateJavascript(FacesContext facesContext,
2941             String hiddenId, String serializedState) throws IOException
2942     {
2943         HtmlJavaScriptUtils.renderViewStateJavascript(facesContext, hiddenId, serializedState);
2944     }
2945 
2946     /**
2947      * Returns the value of the hideNoSelectionOption attribute of the given UIComponent
2948      * @param component
2949      * @return
2950      */
2951     public static boolean isHideNoSelectionOption(UIComponent component)
2952     {
2953         // check hideNoSelectionOption for literal value (String) or ValueExpression (Boolean)
2954         Object hideNoSelectionOptionAttr = component.getAttributes().get(
2955                 JSFAttr.HIDE_NO_SELECTION_OPTION_ATTR);
2956         return ((hideNoSelectionOptionAttr instanceof String && "true"
2957                 .equalsIgnoreCase((String) hideNoSelectionOptionAttr)) || 
2958                 (hideNoSelectionOptionAttr instanceof Boolean && ((Boolean) hideNoSelectionOptionAttr)));
2959     }
2960 
2961     /**
2962      * Renders all FacesMessages which have not been rendered yet with
2963      * the help of a HtmlMessages component.
2964      * @param facesContext
2965      */
2966     public static void renderUnhandledFacesMessages(FacesContext facesContext)
2967             throws IOException
2968     {
2969         // create and configure HtmlMessages component
2970         HtmlMessages messages = (HtmlMessages) facesContext.getApplication()
2971                 .createComponent(HtmlMessages.COMPONENT_TYPE);
2972         messages.setId("javax_faces_developmentstage_messages");
2973         messages.setTitle("Project Stage[Development]: Unhandled Messages");
2974         messages.setStyle("color:orange");
2975         messages.setRedisplay(false);
2976         // render the component
2977         messages.encodeAll(facesContext);
2978     }
2979 
2980     /**
2981      * The ScriptContext offers methods and fields
2982      * to help with rendering out a script and keeping a
2983      * proper formatting.
2984      */
2985     public static class ScriptContext extends JavascriptContext
2986     {
2987         public ScriptContext()
2988         {
2989             super();
2990         }
2991         public ScriptContext(boolean prettyPrint)
2992         {
2993             super(prettyPrint);
2994         }
2995         public ScriptContext(StringBuilder buf, boolean prettyPrint)
2996         {
2997             super(buf, prettyPrint);
2998         }
2999     }
3000 }