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