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