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