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 org.apache.commons.logging.Log;
22  import org.apache.commons.logging.LogFactory;
23  import org.apache.myfaces.shared.component.DisplayValueOnlyCapable;
24  import org.apache.myfaces.shared.component.EscapeCapable;
25  import org.apache.myfaces.shared.config.MyfacesConfig;
26  import org.apache.myfaces.shared.renderkit.JSFAttr;
27  import org.apache.myfaces.shared.renderkit.RendererUtils;
28  import org.apache.myfaces.shared.renderkit.html.util.FormInfo;
29  import org.apache.myfaces.shared.renderkit.html.util.HTMLEncoder;
30  import org.apache.myfaces.shared.renderkit.html.util.JavascriptUtils;
31  
32  import javax.faces.FacesException;
33  import javax.faces.component.*;
34  import javax.faces.component.html.HtmlDataTable;
35  import javax.faces.component.html.HtmlPanelGrid;
36  import javax.faces.context.ExternalContext;
37  import javax.faces.context.FacesContext;
38  import javax.faces.context.ResponseWriter;
39  import javax.faces.convert.Converter;
40  import javax.faces.model.SelectItem;
41  import javax.faces.model.SelectItemGroup;
42  import java.io.IOException;
43  import java.util.*;
44  
45  /***
46   * @author Manfred Geiler (latest modification by $Author: lu4242 $)
47   * @version $Revision: 960240 $ $Date: 2010-07-03 12:30:42 -0500 (Sat, 03 Jul 2010) $
48   */
49  public final class HtmlRendererUtils {
50      private static final Log log = LogFactory.getLog(HtmlRendererUtils.class);
51  
52      //private static final String[] EMPTY_STRING_ARRAY = new String[0];
53      private static final String LINE_SEPARATOR = System.getProperty(
54              "line.separator", "\r\n");
55      private static final char TABULATOR = '\t';
56  
57      public static final String HIDDEN_COMMANDLINK_FIELD_NAME = "_idcl";
58      public static final String HIDDEN_COMMANDLINK_FIELD_NAME_MYFACES_OLD = "_link_hidden_";
59      public static final String HIDDEN_COMMANDLINK_FIELD_NAME_TRINIDAD = "source";
60  
61      public static final String CLEAR_HIDDEN_FIELD_FN_NAME =
62          "clearFormHiddenParams";
63      public static final String SUBMIT_FORM_FN_NAME = "oamSubmitForm";
64      public static final String ALLOW_CDATA_SECTION_ON = "org.apache.myfaces.ResponseWriter.CdataSectionOn";
65  
66      private static final String SET_HIDDEN_INPUT_FN_NAME = "oamSetHiddenInput";
67      private static final String CLEAR_HIDDEN_INPUT_FN_NAME = "oamClearHiddenInput";
68  
69      private static final String AUTO_SCROLL_PARAM = "autoScroll";
70      private static final String AUTO_SCROLL_FUNCTION = "getScrolling";
71  
72      private static final String FIRST_SUBMIT_SCRIPT_ON_PAGE = "org.apache.MyFaces.FIRST_SUBMIT_SCRIPT_ON_PAGE";
73  
74      public static final String NON_SUBMITTED_VALUE_WARNING
75      = "There should always be a submitted value for an input if it is rendered,"
76      + " its form is submitted, and it was not originally rendered disabled or read-only."
77      + "  You cannot submit a form after disabling an input element via javascript."
78      + "  Consider setting read-only to true instead"
79      + " or resetting the disabled value back to false prior to form submission.";
80  
81  
82      private HtmlRendererUtils() {
83          // utility class, do not instantiate
84      }
85  
86      /***
87       * Utility to set the submitted value of the provided component from the
88       * data in the current request object.
89       * <p>
90       * Param component is required to be an EditableValueHolder. On return
91       * from this method, the component's submittedValue property will be
92       * set if the submitted form contained that component.
93       */
94      public static void decodeUIInput(FacesContext facesContext,
95                                       UIComponent component) {
96          if (!(component instanceof EditableValueHolder)) {
97              throw new IllegalArgumentException("Component "
98                                                 + component.getClientId(facesContext)
99                                                 + " is not an EditableValueHolder");
100         }
101         Map paramMap = facesContext.getExternalContext()
102                 .getRequestParameterMap();
103         String clientId = component.getClientId(facesContext);
104 
105         if(isDisabledOrReadOnly(component))
106             return;
107 
108         if(paramMap.containsKey(clientId))
109         {
110             ((EditableValueHolder) component).setSubmittedValue(paramMap
111                     .get(clientId));
112         }
113         else {
114             log.warn(NON_SUBMITTED_VALUE_WARNING +
115                 " Component : " +
116                     RendererUtils.getPathToComponent(component));
117         }
118     }
119 
120     /***
121      * X-CHECKED: tlddoc h:selectBooleanCheckbox
122      *
123      * @param facesContext
124      * @param component
125      */
126     public static void decodeUISelectBoolean(FacesContext facesContext,
127                                              UIComponent component) {
128         if (!(component instanceof EditableValueHolder)) {
129             throw new IllegalArgumentException("Component "
130                                                + component.getClientId(facesContext)
131                                                + " is not an EditableValueHolder");
132         }
133 
134         if(isDisabledOrReadOnly(component))
135             return;
136 
137         Map paramMap = facesContext.getExternalContext()
138                 .getRequestParameterMap();
139         String clientId = component.getClientId(facesContext);
140         if (paramMap.containsKey(clientId)) {
141             String reqValue = (String) paramMap.get(clientId);
142             if ((reqValue.equalsIgnoreCase("on")
143                  || reqValue.equalsIgnoreCase("yes") || reqValue
144                     .equalsIgnoreCase("true"))) {
145                 ((EditableValueHolder) component)
146                         .setSubmittedValue(Boolean.TRUE);
147             } else {
148                 ((EditableValueHolder) component)
149                         .setSubmittedValue(Boolean.FALSE);
150             }
151         } else {
152             ((EditableValueHolder) component)
153                     .setSubmittedValue(Boolean.FALSE);
154         }
155     }
156 
157     public static boolean isDisabledOrReadOnly(UIComponent component)
158     {
159         return isDisplayValueOnly(component) ||
160             isDisabled(component) ||
161             isReadOnly(component);
162     }
163 
164     public static boolean isDisabled(UIComponent component)
165     {
166         return isTrue(component.getAttributes().get("disabled"));
167     }
168 
169     public static boolean isReadOnly(UIComponent component)
170     {
171         return isTrue(component.getAttributes().get("readonly"));
172     }
173 
174     private static boolean isTrue(Object obj)
175     {
176         if(!(obj instanceof Boolean))
177             return false;
178 
179         return ((Boolean) obj).booleanValue();
180     }
181 
182     /***
183      * X-CHECKED: tlddoc h:selectManyListbox
184      *
185      * @param facesContext
186      * @param component
187      */
188     public static void decodeUISelectMany(FacesContext facesContext,
189                                           UIComponent component) {
190         if (!(component instanceof EditableValueHolder)) {
191             throw new IllegalArgumentException("Component "
192                                                + component.getClientId(facesContext)
193                                                + " is not an EditableValueHolder");
194         }
195         Map paramValuesMap = facesContext.getExternalContext()
196                 .getRequestParameterValuesMap();
197         String clientId = component.getClientId(facesContext);
198 
199         if(isDisabledOrReadOnly(component))
200             return;
201 
202         if (paramValuesMap.containsKey(clientId)) {
203             String[] reqValues = (String[]) paramValuesMap.get(clientId);
204             ((EditableValueHolder) component).setSubmittedValue(reqValues);
205         } else {
206             /* request parameter not found, nothing to decode - set submitted value to an empty array
207                as we should get here only if the component is on a submitted form, is rendered
208                and if the component is not readonly or has not been disabled.
209 
210                So in fact, there must be component value at this location, but for listboxes, comboboxes etc.
211                the submitted value is not posted if no item is selected. */
212             ((EditableValueHolder) component).setSubmittedValue( new String[]{});
213         }
214     }
215 
216     /***
217      * X-CHECKED: tlddoc h:selectManyListbox
218      *
219      * @param facesContext
220      * @param component
221      */
222     public static void decodeUISelectOne(FacesContext facesContext,
223                                          UIComponent component) {
224         if (!(component instanceof EditableValueHolder)) {
225             throw new IllegalArgumentException("Component "
226                                                + component.getClientId(facesContext)
227                                                + " is not an EditableValueHolder");
228         }
229 
230         if(isDisabledOrReadOnly(component))
231             return;
232 
233         Map paramMap = facesContext.getExternalContext()
234                 .getRequestParameterMap();
235         String clientId = component.getClientId(facesContext);
236         if (paramMap.containsKey(clientId)) {
237             //request parameter found, set submitted value
238             ((EditableValueHolder) component).setSubmittedValue(paramMap
239                     .get(clientId));
240         } else {
241             //see reason for this action at decodeUISelectMany
242             ((EditableValueHolder) component).setSubmittedValue( RendererUtils.NOTHING );
243         }
244     }
245 
246     /*
247      * public static void renderCheckbox(FacesContext facesContext, UIComponent
248      * uiComponent, String value, String label, boolean checked) throws
249      * IOException { String clientId = uiComponent.getClientId(facesContext);
250      *
251      * ResponseWriter writer = facesContext.getResponseWriter();
252      *
253      * writer.startElement(HTML.INPUT_ELEM, uiComponent);
254      * writer.writeAttribute(HTML.TYPE_ATTR, HTML.INPUT_TYPE_CHECKBOX, null);
255      * writer.writeAttribute(HTML.NAME_ATTR, clientId, null);
256      * writer.writeAttribute(HTML.ID_ATTR, clientId, null);
257      *
258      * if (checked) { writer.writeAttribute(HTML.CHECKED_ATTR,
259      * HTML.CHECKED_ATTR, null); }
260      *
261      * if ((value != null) && (value.length() > 0)) {
262      * writer.writeAttribute(HTML.VALUE_ATTR, value, null); }
263      *
264      * renderHTMLAttributes(writer, uiComponent,
265      * HTML.INPUT_PASSTHROUGH_ATTRIBUTES); renderDisabledOnUserRole(writer,
266      * uiComponent, facesContext);
267      *
268      * if ((label != null) && (label.length() > 0)) {
269      * writer.write(HTML.NBSP_ENTITY); writer.writeText(label, null); }
270      *
271      * writer.endElement(HTML.INPUT_ELEM); }
272      */
273 
274     public static void renderListbox(FacesContext facesContext,
275                                      UISelectOne selectOne, boolean disabled, int size)
276             throws IOException {
277         internalRenderSelect(facesContext, selectOne, disabled, size, false);
278     }
279 
280     public static void renderListbox(FacesContext facesContext,
281                                      UISelectMany selectMany, boolean disabled, int size)
282             throws IOException {
283         internalRenderSelect(facesContext, selectMany, disabled, size, true);
284     }
285 
286     public static void renderMenu(FacesContext facesContext,
287                                   UISelectOne selectOne, boolean disabled) throws IOException {
288         internalRenderSelect(facesContext, selectOne, disabled, 1, false);
289     }
290 
291     public static void renderMenu(FacesContext facesContext,
292                                   UISelectMany selectMany, boolean disabled) throws IOException {
293         internalRenderSelect(facesContext, selectMany, disabled, 1, true);
294     }
295 
296     private static void internalRenderSelect(FacesContext facesContext,
297                                              UIComponent uiComponent, boolean disabled, int size,
298                                              boolean selectMany) throws IOException {
299         ResponseWriter writer = facesContext.getResponseWriter();
300 
301         writer.startElement(HTML.SELECT_ELEM, uiComponent);
302         HtmlRendererUtils.writeIdIfNecessary(writer, uiComponent, facesContext);
303         writer.writeAttribute(HTML.NAME_ATTR, uiComponent
304                 .getClientId(facesContext), null);
305 
306         List selectItemList;
307         Converter converter;
308         if (selectMany) {
309             writer.writeAttribute(HTML.MULTIPLE_ATTR, HTML.MULTIPLE_ATTR, null);
310             selectItemList = org.apache.myfaces.shared.renderkit.RendererUtils
311                     .getSelectItemList((UISelectMany) uiComponent);
312             converter = findUISelectManyConverterFailsafe(facesContext, uiComponent);
313         } else {
314             selectItemList = RendererUtils
315                     .getSelectItemList((UISelectOne) uiComponent);
316             converter = findUIOutputConverterFailSafe(facesContext, uiComponent);
317         }
318 
319         if (size == Integer.MIN_VALUE) {
320             //No size given (Listbox) --> size is number of select items
321             writer.writeAttribute(HTML.SIZE_ATTR, Integer
322                     .toString(selectItemList.size()), null);
323         } else {
324             writer.writeAttribute(HTML.SIZE_ATTR, Integer.toString(size), null);
325         }
326         renderHTMLAttributes(writer, uiComponent,
327                              HTML.SELECT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED);
328         if (disabled) {
329             writer.writeAttribute(HTML.DISABLED_ATTR, Boolean.TRUE, null);
330         }
331 
332         if (isReadOnly(uiComponent))
333         {
334             writer.writeAttribute(HTML.READONLY_ATTR, HTML.READONLY_ATTR, null);
335         }
336 
337         Set lookupSet = getSubmittedOrSelectedValuesAsSet(selectMany, uiComponent, facesContext, converter);
338 
339         renderSelectOptions(facesContext, uiComponent, converter, lookupSet,
340                             selectItemList);
341         // bug #970747: force separate end tag
342         writer.writeText("", null);
343         writer.endElement(HTML.SELECT_ELEM);
344     }
345 
346     public static Set getSubmittedOrSelectedValuesAsSet(boolean selectMany, UIComponent uiComponent, FacesContext facesContext, Converter converter) {
347         Set lookupSet;
348 
349         if (selectMany) {
350             UISelectMany uiSelectMany = (UISelectMany) uiComponent;
351             lookupSet = RendererUtils.getSubmittedValuesAsSet(facesContext, uiComponent, converter, uiSelectMany);
352             if (lookupSet == null)
353             {
354                 lookupSet = RendererUtils.getSelectedValuesAsSet(facesContext, uiComponent, converter, uiSelectMany);
355             }
356         } else {
357             UISelectOne uiSelectOne = (UISelectOne) uiComponent;
358             Object lookup = uiSelectOne.getSubmittedValue();
359             if (lookup == null)
360             {
361                 lookup = uiSelectOne.getValue();
362                 String lookupString = RendererUtils.getConvertedStringValue(facesContext, uiComponent, converter, lookup);
363                 lookupSet = Collections.singleton(lookupString);
364             }
365             else if(org.apache.myfaces.shared.renderkit.RendererUtils.NOTHING.equals(lookup))
366             {
367                 lookupSet = Collections.EMPTY_SET;
368             }
369             else
370             {
371                 lookupSet = Collections.singleton(lookup);
372             }
373         }
374         return lookupSet;
375     }
376 
377     public static Converter findUISelectManyConverterFailsafe(FacesContext facesContext, UIComponent uiComponent) {
378         Converter converter;
379         try {
380             converter = RendererUtils.findUISelectManyConverter(
381                     facesContext, (UISelectMany) uiComponent);
382         } catch (FacesException e) {
383             log.error("Error finding Converter for component with id "
384                       + uiComponent.getClientId(facesContext), e);
385             converter = null;
386         }
387         return converter;
388     }
389 
390     public static Converter findUIOutputConverterFailSafe(FacesContext facesContext, UIComponent uiComponent) {
391         Converter converter;
392         try {
393             converter = RendererUtils.findUIOutputConverter(facesContext,
394                                                             (UIOutput) uiComponent);
395         } catch (FacesException e) {
396             log.error("Error finding Converter for component with id "
397                       + uiComponent.getClientId(facesContext), e);
398             converter = null;
399         }
400         return converter;
401     }
402 
403     /***
404      * Renders the select options for a <code>UIComponent</code> that is
405      * rendered as an HTML select element.
406      *
407      * @param context
408      *            the current <code>FacesContext</code>.
409      * @param component
410      *            the <code>UIComponent</code> whose options need to be
411      *            rendered.
412      * @param converter
413      *            <code>component</code>'s converter
414      * @param lookupSet
415      *            the <code>Set</code> to use to look up selected options
416      * @param selectItemList
417      *            the <code>List</code> of <code>SelectItem</code> s to be
418      *            rendered as HTML option elements.
419      * @throws IOException
420      */
421     public static void renderSelectOptions(FacesContext context,
422                                            UIComponent component, Converter converter, Set lookupSet,
423                                            List selectItemList) throws IOException {
424         ResponseWriter writer = context.getResponseWriter();
425 
426         for (Iterator it = selectItemList.iterator(); it.hasNext();) {
427             SelectItem selectItem = (SelectItem) it.next();
428 
429             if (selectItem instanceof SelectItemGroup) {
430                 writer.startElement(HTML.OPTGROUP_ELEM, component);
431                 writer.writeAttribute(HTML.LABEL_ATTR, selectItem.getLabel(),
432                                       null);
433                 SelectItem[] selectItems = ((SelectItemGroup) selectItem)
434                         .getSelectItems();
435                 renderSelectOptions(context, component, converter, lookupSet,
436                                     Arrays.asList(selectItems));
437                 writer.endElement(HTML.OPTGROUP_ELEM);
438             } else {
439                 String itemStrValue = org.apache.myfaces.shared.renderkit.RendererUtils.getConvertedStringValue(context, component,
440                                                                             converter, selectItem);
441 
442                 writer.write(TABULATOR);
443                 writer.startElement(HTML.OPTION_ELEM, component);
444                 if (itemStrValue != null) {
445                     writer.writeAttribute(HTML.VALUE_ATTR, itemStrValue, null);
446                 }
447 
448                 if (lookupSet.contains(itemStrValue)) {  //TODO/FIX: we always compare the String vales, better fill lookupSet with Strings only when useSubmittedValue==true, else use the real item value Objects
449                     writer.writeAttribute(HTML.SELECTED_ATTR,
450                                           HTML.SELECTED_ATTR, null);
451                 }
452 
453                 boolean disabled = selectItem.isDisabled();
454                 if (disabled) {
455                     writer.writeAttribute(HTML.DISABLED_ATTR,
456                                           HTML.DISABLED_ATTR, null);
457                 }
458 
459                 String labelClass = null;
460                 boolean componentDisabled = isTrue(component.getAttributes().get("disabled"));
461 
462                 if (componentDisabled || disabled) {
463                     labelClass = (String) component.getAttributes().get(JSFAttr.DISABLED_CLASS_ATTR);
464                 } else {
465                     labelClass = (String) component.getAttributes().get(JSFAttr.ENABLED_CLASS_ATTR);
466                 }
467                 if (labelClass != null) {
468                     writer.writeAttribute("class", labelClass, "labelClass");
469                 }
470 
471                 boolean escape;
472                 if (component instanceof EscapeCapable)
473                 {
474                     escape = ((EscapeCapable)component).isEscape();
475                     
476                     // Preserve tomahawk semantic. If escape=false
477                     // all items should be non escaped. If escape
478                     // is true check if selectItem.isEscape() is
479                     // true and do it.
480                     // This is done for remain compatibility.
481                     if (escape && selectItem.isEscape())
482                     {
483                         writer.writeText(selectItem.getLabel(), null);
484                     } else
485                     {
486                         writer.write(selectItem.getLabel());
487                     }
488                 }
489                 else
490                 {
491                     escape = RendererUtils.getBooleanAttribute(component, JSFAttr.ESCAPE_ATTR,
492                                                                false);
493                     //default is to escape
494                     //In JSF 1.2, when a SelectItem is created by default 
495                     //selectItem.isEscape() returns true (this property
496                     //is not available on JSF 1.1).
497                     //so, if we found a escape property on the component
498                     //set to true, escape every item, but if not
499                     //check if isEscape() = true first.
500                     if (escape || selectItem.isEscape())
501                     {
502                         writer.writeText(selectItem.getLabel(), null);
503                     } else
504                     {
505                         writer.write(selectItem.getLabel());
506                     }
507                 }
508 
509                 writer.endElement(HTML.OPTION_ELEM);
510             }
511         }
512     }
513 
514     /*
515      * public static void renderRadio(FacesContext facesContext, UIInput
516      * uiComponent, String value, String label, boolean checked) throws
517      * IOException { String clientId = uiComponent.getClientId(facesContext);
518      *
519      * ResponseWriter writer = facesContext.getResponseWriter();
520      *
521      * writer.startElement(HTML.INPUT_ELEM, uiComponent);
522      * writer.writeAttribute(HTML.TYPE_ATTR, HTML.INPUT_TYPE_RADIO, null);
523      * writer.writeAttribute(HTML.NAME_ATTR, clientId, null);
524      * writer.writeAttribute(HTML.ID_ATTR, clientId, null);
525      *
526      * if (checked) { writer.writeAttribute(HTML.CHECKED_ATTR,
527      * HTML.CHECKED_ATTR, null); }
528      *
529      * if ((value != null) && (value.length() > 0)) {
530      * writer.writeAttribute(HTML.VALUE_ATTR, value, null); }
531      *
532      * renderHTMLAttributes(writer, uiComponent,
533      * HTML.INPUT_PASSTHROUGH_ATTRIBUTES); renderDisabledOnUserRole(writer,
534      * uiComponent, facesContext);
535      *
536      * if ((label != null) && (label.length() > 0)) {
537      * writer.write(HTML.NBSP_ENTITY); writer.writeText(label, null); }
538      *
539      * writer.endElement(HTML.INPUT_ELEM); }
540      */
541 
542     public static void writePrettyLineSeparator(FacesContext facesContext)
543             throws IOException {
544         if (org.apache.myfaces.shared.config.MyfacesConfig.getCurrentInstance(facesContext.getExternalContext())
545                 .isPrettyHtml()) {
546             facesContext.getResponseWriter().write(LINE_SEPARATOR);
547         }
548     }
549 
550     public static void writePrettyIndent(FacesContext facesContext)
551             throws IOException {
552         if (org.apache.myfaces.shared.config.MyfacesConfig.getCurrentInstance(facesContext.getExternalContext())
553                 .isPrettyHtml()) {
554             facesContext.getResponseWriter().write('\t');
555         }
556     }
557 
558     /***
559      * @return true, if the attribute was written
560      * @throws java.io.IOException
561      */
562     public static boolean renderHTMLAttribute(ResponseWriter writer,
563                                               String componentProperty, String attrName, Object value)
564             throws IOException {
565         if (!RendererUtils.isDefaultAttributeValue(value)) {
566             // render JSF "styleClass" and "itemStyleClass" attributes as "class"
567             String htmlAttrName =
568                     attrName.equals(HTML.STYLE_CLASS_ATTR) ?
569                     HTML.CLASS_ATTR : attrName;
570             writer.writeAttribute(htmlAttrName, value, componentProperty);
571             return true;
572         }
573 
574         return false;
575     }
576 
577     /***
578      * @return true, if the attribute was written
579      * @throws java.io.IOException
580      */
581     public static boolean renderHTMLAttribute(ResponseWriter writer,
582                                               UIComponent component, String componentProperty, String htmlAttrName)
583             throws IOException {
584         Object value = component.getAttributes().get(componentProperty);
585         return renderHTMLAttribute(writer, componentProperty, htmlAttrName,
586                                    value);
587     }
588 
589     /***
590      * @return true, if an attribute was written
591      * @throws java.io.IOException
592      */
593     public static boolean renderHTMLAttributes(ResponseWriter writer,
594                                                UIComponent component, String[] attributes) throws IOException {
595         boolean somethingDone = false;
596         for (int i = 0, len = attributes.length; i < len; i++) {
597             String attrName = attributes[i];
598             if (renderHTMLAttribute(writer, component, attrName, attrName)) {
599                 somethingDone = true;
600             }
601         }
602         return somethingDone;
603     }
604 
605     public static boolean renderHTMLAttributeWithOptionalStartElement(
606             ResponseWriter writer, UIComponent component, String elementName,
607             String attrName, Object value, boolean startElementWritten)
608             throws IOException {
609         if (!org.apache.myfaces.shared.renderkit.RendererUtils.isDefaultAttributeValue(value)) {
610             if (!startElementWritten) {
611                 writer.startElement(elementName, component);
612                 startElementWritten = true;
613             }
614             renderHTMLAttribute(writer, attrName, attrName, value);
615         }
616         return startElementWritten;
617     }
618 
619     public static boolean renderHTMLAttributesWithOptionalStartElement(
620             ResponseWriter writer, UIComponent component, String elementName,
621             String[] attributes) throws IOException {
622         boolean startElementWritten = false;
623         for (int i = 0, len = attributes.length; i < len; i++) {
624             String attrName = attributes[i];
625             Object value = component.getAttributes().get(attrName);
626             if (!RendererUtils.isDefaultAttributeValue(value)) {
627                 if (!startElementWritten) {
628                     writer.startElement(elementName, component);
629                     startElementWritten = true;
630                 }
631                 renderHTMLAttribute(writer, attrName, attrName, value);
632             }
633         }
634         return startElementWritten;
635     }
636 
637     public static boolean renderOptionalEndElement(ResponseWriter writer,
638                                                    UIComponent component, String elementName, String[] attributes)
639             throws IOException {
640         boolean endElementNeeded = false;
641         for (int i = 0, len = attributes.length; i < len; i++) {
642             String attrName = attributes[i];
643             Object value = component.getAttributes().get(attrName);
644             if (!RendererUtils.isDefaultAttributeValue(value)) {
645                 endElementNeeded = true;
646                 break;
647             }
648         }
649         if (endElementNeeded) {
650             writer.endElement(elementName);
651             return true;
652         }
653 
654         return false;
655     }
656 
657     public static void writeIdIfNecessary(ResponseWriter writer, UIComponent component,
658                                           FacesContext facesContext)
659             throws IOException
660     {
661         if(component.getId()!=null && !component.getId().startsWith(UIViewRoot.UNIQUE_ID_PREFIX))
662         {
663             writer.writeAttribute(HTML.ID_ATTR, component.getClientId(facesContext),null);
664         }
665     }
666 
667     public static void writeIdAndNameIfNecessary(ResponseWriter writer, UIComponent component,
668                                                  FacesContext facesContext)
669             throws IOException
670     {
671         if(component.getId()!=null && !component.getId().startsWith(UIViewRoot.UNIQUE_ID_PREFIX))
672         {
673             String clientId = component.getClientId(facesContext);
674             writer.writeAttribute(HTML.ID_ATTR, clientId, null);
675             writer.writeAttribute(HTML.NAME_ATTR, clientId, null);
676         }
677     }
678 
679     public static void renderDisplayValueOnlyForSelects(FacesContext facesContext, UIComponent
680             uiComponent)
681             throws IOException
682     {
683         ResponseWriter writer = facesContext.getResponseWriter();
684 
685         List selectItemList=null;
686         Converter converter=null;
687         boolean isSelectOne=false;
688 
689         if(uiComponent instanceof UISelectBoolean)
690         {
691             converter = findUIOutputConverterFailSafe(facesContext, uiComponent);
692 
693             writer.startElement(HTML.SPAN_ELEM, uiComponent);
694             writeIdIfNecessary(writer, uiComponent, facesContext);
695             renderDisplayValueOnlyAttributes(uiComponent, writer);
696             writer.writeText(RendererUtils.getConvertedStringValue(facesContext,uiComponent,
697                                                                    converter,((UISelectBoolean) uiComponent).getValue()),JSFAttr.VALUE_ATTR);
698             writer.endElement(HTML.SPAN_ELEM);
699 
700         }
701         else
702         {
703             if (uiComponent instanceof UISelectMany) {
704                 isSelectOne = false;
705                 selectItemList = RendererUtils
706                         .getSelectItemList((UISelectMany) uiComponent);
707                 converter = findUISelectManyConverterFailsafe(facesContext, uiComponent);
708             } else if(uiComponent instanceof UISelectOne){
709                 isSelectOne = true;
710                 selectItemList = RendererUtils
711                         .getSelectItemList((UISelectOne) uiComponent);
712                 converter = findUIOutputConverterFailSafe(facesContext, uiComponent);
713             }
714 
715             writer.startElement(isSelectOne ? HTML.SPAN_ELEM : HTML.UL_ELEM, uiComponent);
716             writeIdIfNecessary(writer, uiComponent, facesContext);
717 
718             renderDisplayValueOnlyAttributes(uiComponent, writer);
719 
720             Set lookupSet = getSubmittedOrSelectedValuesAsSet(
721                     uiComponent instanceof UISelectMany,
722                     uiComponent, facesContext, converter);
723 
724             renderSelectOptionsAsText(facesContext, uiComponent, converter, lookupSet,
725                                       selectItemList, isSelectOne);
726 
727             // bug #970747: force separate end tag
728             writer.writeText("", null);
729             writer.endElement(isSelectOne ? HTML.SPAN_ELEM : HTML.UL_ELEM);
730         }
731 
732     }
733 
734     public static void renderDisplayValueOnlyAttributes(UIComponent uiComponent, ResponseWriter writer) throws IOException {
735         if(!(uiComponent instanceof org.apache.myfaces.shared.component.DisplayValueOnlyCapable))
736         {
737             log.error("Wrong type of uiComponent. needs DisplayValueOnlyCapable.");
738             renderHTMLAttributes(writer, uiComponent,
739                                  HTML.COMMON_PASSTROUGH_ATTRIBUTES);
740 
741             return;
742         }
743 
744         if(getDisplayValueOnlyStyle(uiComponent) != null || getDisplayValueOnlyStyleClass(uiComponent)!=null)
745         {
746             if(getDisplayValueOnlyStyle(uiComponent) != null )
747             {
748                 writer.writeAttribute(HTML.STYLE_ATTR, getDisplayValueOnlyStyle(uiComponent), null);
749             }
750             else if(uiComponent.getAttributes().get("style")!=null)
751             {
752                 writer.writeAttribute(HTML.STYLE_ATTR, uiComponent.getAttributes().get("style"), null);
753             }
754 
755             if(getDisplayValueOnlyStyleClass(uiComponent) != null )
756             {
757                 writer.writeAttribute(HTML.CLASS_ATTR, getDisplayValueOnlyStyleClass(uiComponent), null);
758             }
759             else if(uiComponent.getAttributes().get("styleClass")!=null)
760             {
761                 writer.writeAttribute(HTML.CLASS_ATTR, uiComponent.getAttributes().get("styleClass"), null);
762             }
763 
764             renderHTMLAttributes(writer, uiComponent,
765                                  HTML.COMMON_PASSTROUGH_ATTRIBUTES_WITHOUT_STYLE);
766         }
767         else
768         {
769             renderHTMLAttributes(writer, uiComponent,
770                                  HTML.COMMON_PASSTROUGH_ATTRIBUTES);
771         }
772     }
773 
774     private static void renderSelectOptionsAsText(FacesContext context,
775                                                   UIComponent component, Converter converter, Set lookupSet,
776                                                   List selectItemList, boolean isSelectOne) throws IOException {
777         ResponseWriter writer = context.getResponseWriter();
778 
779         for (Iterator it = selectItemList.iterator(); it.hasNext();) {
780             SelectItem selectItem = (SelectItem) it.next();
781 
782             if (selectItem instanceof SelectItemGroup) {
783                 SelectItem[] selectItems = ((SelectItemGroup) selectItem)
784                         .getSelectItems();
785                 renderSelectOptionsAsText(context, component, converter, lookupSet,
786                                           Arrays.asList(selectItems), isSelectOne);
787             } else {
788                 String itemStrValue = RendererUtils.getConvertedStringValue(context, component,
789                                                                             converter, selectItem);
790 
791                 if (lookupSet.contains(itemStrValue)) {  //TODO/FIX: we always compare the String vales, better fill lookupSet with Strings only when useSubmittedValue==true, else use the real item value Objects
792 
793                     if( ! isSelectOne )
794                         writer.startElement(HTML.LI_ELEM, component);
795                     writer.writeText(selectItem.getLabel(), null);
796                     if( ! isSelectOne )
797                         writer.endElement(HTML.LI_ELEM);
798 
799                     if( isSelectOne )
800                     {
801                         //take care of several choices with the same value; use only the first one
802                         return;
803                     }
804                 }
805             }
806         }
807     }
808 
809     public static void renderTableCaption(FacesContext context,
810                                  ResponseWriter writer,
811                                  UIComponent component)
812             throws IOException
813     {
814         UIComponent captionFacet = component.getFacet("caption");
815         if (captionFacet == null) return;
816 
817         String captionClass;
818         String captionStyle;
819 
820         if (component instanceof HtmlPanelGrid)
821         {
822             HtmlPanelGrid panelGrid = (HtmlPanelGrid) component;
823             captionClass = panelGrid.getCaptionClass();
824             captionStyle = panelGrid.getCaptionStyle();
825         }
826         else if (component instanceof HtmlDataTable)
827         {
828             HtmlDataTable dataTable = (HtmlDataTable) component;
829             captionClass = dataTable.getCaptionClass();
830             captionStyle = dataTable.getCaptionStyle();
831         }
832         else
833         {
834             captionClass = (String)component.getAttributes().get(org.apache.myfaces.shared.renderkit.JSFAttr.CAPTION_CLASS_ATTR);
835             captionStyle = (String)component.getAttributes().get(org.apache.myfaces.shared.renderkit.JSFAttr.CAPTION_STYLE_ATTR);
836         }
837 
838         HtmlRendererUtils.writePrettyLineSeparator(context);
839         writer.startElement(HTML.CAPTION_ELEM, component);
840 
841         if (captionClass != null)
842         {
843             writer.writeAttribute(HTML.CLASS_ATTR, captionClass, null);
844         }
845 
846         if (captionStyle != null)
847         {
848             writer.writeAttribute(HTML.STYLE_ATTR, captionStyle, null);
849         }
850 
851         RendererUtils.renderChild(context, captionFacet);
852 
853         writer.endElement(HTML.CAPTION_ELEM);
854     }
855 
856     public static String getDisplayValueOnlyStyleClass(UIComponent component) {
857 
858         if(component instanceof org.apache.myfaces.shared.component.DisplayValueOnlyCapable)
859         {
860             if(((org.apache.myfaces.shared.component.DisplayValueOnlyCapable) component).getDisplayValueOnlyStyleClass()!=null)
861                 return ((org.apache.myfaces.shared.component.DisplayValueOnlyCapable) component).getDisplayValueOnlyStyleClass();
862 
863             UIComponent parent=component;
864 
865             while((parent = parent.getParent())!=null)
866             {
867                 if(parent instanceof org.apache.myfaces.shared.component.DisplayValueOnlyCapable &&
868                    ((org.apache.myfaces.shared.component.DisplayValueOnlyCapable) parent).getDisplayValueOnlyStyleClass()!=null)
869                 {
870                     return ((org.apache.myfaces.shared.component.DisplayValueOnlyCapable) parent).getDisplayValueOnlyStyleClass();
871                 }
872             }
873         }
874 
875         return null;
876     }
877 
878     public static String getDisplayValueOnlyStyle(UIComponent component) {
879 
880         if(component instanceof DisplayValueOnlyCapable)
881         {
882             if(((org.apache.myfaces.shared.component.DisplayValueOnlyCapable) component).getDisplayValueOnlyStyle()!=null)
883                 return ((org.apache.myfaces.shared.component.DisplayValueOnlyCapable) component).getDisplayValueOnlyStyle();
884 
885             UIComponent parent=component;
886 
887             while((parent = parent.getParent())!=null)
888             {
889                 if(parent instanceof org.apache.myfaces.shared.component.DisplayValueOnlyCapable &&
890                    ((DisplayValueOnlyCapable) parent).getDisplayValueOnlyStyle()!=null)
891                 {
892                     return ((DisplayValueOnlyCapable) parent).getDisplayValueOnlyStyle();
893                 }
894             }
895         }
896 
897         return null;
898     }
899 
900     public static boolean isDisplayValueOnly(UIComponent component) {
901 
902         if(component instanceof DisplayValueOnlyCapable)
903         {
904             if(((DisplayValueOnlyCapable) component).isSetDisplayValueOnly())
905                 return ((org.apache.myfaces.shared.component.DisplayValueOnlyCapable) component).isDisplayValueOnly();
906 
907             UIComponent parent=component;
908 
909             while((parent = parent.getParent())!=null)
910             {
911                 if(parent instanceof DisplayValueOnlyCapable &&
912                    ((DisplayValueOnlyCapable) parent).isSetDisplayValueOnly())
913                 {
914                     return ((org.apache.myfaces.shared.component.DisplayValueOnlyCapable) parent).isDisplayValueOnly();
915                 }
916             }
917         }
918 
919         return false;
920     }
921 
922     public static void renderDisplayValueOnly(FacesContext facesContext, UIInput input) throws IOException {
923         ResponseWriter writer = facesContext.getResponseWriter();
924         writer.startElement(org.apache.myfaces.shared.renderkit.html.HTML.SPAN_ELEM, input);
925 
926         writeIdIfNecessary(writer, input, facesContext);
927 
928         renderDisplayValueOnlyAttributes(input, writer);
929 
930         String strValue = RendererUtils.getStringValue(facesContext, input);
931         writer.write( HTMLEncoder.encode(strValue, true, true) );
932 
933         writer.endElement(HTML.SPAN_ELEM);
934     }
935 
936     public static void appendClearHiddenCommandFormParamsFunctionCall(StringBuffer buf, String formName) {
937         appendClearHiddenCommandFormParamsFunctionCall(new ScriptContext(buf,false), formName);
938     }
939 
940     private static void appendClearHiddenCommandFormParamsFunctionCall(ScriptContext context, String formName) {
941 
942         String functionName = HtmlRendererUtils.getClearHiddenCommandFormParamsFunctionName(formName);
943 
944         if(formName == null)
945         {
946             context.prettyLine();
947             context.append("var clearFn = ");
948             context.append(functionName);
949             context.append(";");
950             context.prettyLine();
951             context.append("if(typeof window[clearFn] =='function')");
952             context.append("{");
953             context.append("window[clearFn](formName);");
954             context.append("}");
955         }
956         else
957         {
958             context.prettyLine();
959             context.append("if(typeof window.");
960             context.append(functionName);
961             context.append("=='function')");
962             context.append("{");
963             context.append(functionName).append("('").append(formName).append("');");
964             context.append("}");
965         }
966     }
967 
968 
969     public static void renderFormSubmitScript(FacesContext facesContext)
970             throws IOException
971     {
972 
973         Map map = facesContext.getExternalContext().getRequestMap();
974         Boolean firstScript = (Boolean) map.get(FIRST_SUBMIT_SCRIPT_ON_PAGE);
975 
976         if (firstScript == null || firstScript.equals(Boolean.TRUE)) {
977             map.put(FIRST_SUBMIT_SCRIPT_ON_PAGE, Boolean.FALSE);
978             HtmlRendererUtils.renderFormSubmitScriptIfNecessary(facesContext);
979 
980         }
981     }
982 
983     private static void renderFormSubmitScriptIfNecessary(FacesContext facesContext) throws IOException {
984         ResponseWriter writer = facesContext.getResponseWriter();
985 
986         writer.startElement(HTML.SCRIPT_ELEM, null);
987         writer.writeAttribute(HTML.TYPE_ATTR, "text/javascript", null);
988 
989         final ExternalContext externalContext = facesContext.getExternalContext();
990         final MyfacesConfig currentInstance = MyfacesConfig.getCurrentInstance(externalContext);
991         boolean autoScroll = currentInstance.isAutoScroll();
992 
993         ScriptContext context = new ScriptContext(currentInstance
994             .isPrettyHtml());
995         context.prettyLine();
996         context.increaseIndent();
997 
998         prepareScript(context, autoScroll);
999 
1000         writer.writeText(context.toString(),null);
1001 
1002         writer.endElement(HTML.SCRIPT_ELEM);
1003     }
1004 
1005     private static void prepareScript(ScriptContext context, boolean autoScroll)
1006     {
1007 
1008         context.prettyLine();
1009 
1010         //render a function to create a hidden input, if it doesn't exist
1011         context.append("function ");
1012         context.append(SET_HIDDEN_INPUT_FN_NAME).append("(formname, name, value)");
1013         context.append("{");
1014         context.append("var form = document.forms[formname];");
1015         context.prettyLine();
1016         context.append("if (typeof form == 'undefined')");
1017         context.append("{");
1018         context.append("form = document.getElementById(formname);");
1019         context.append("}");
1020         context.prettyLine();
1021         context.append("if(typeof form.elements[name]!='undefined' && (form.elements[name].nodeName=='INPUT' || form.elements[name].nodeName=='input'))");
1022         context.append("{");
1023         context.append("form.elements[name].value=value;");        
1024         context.append("}");
1025         context.append("else");
1026         context.append("{");
1027         context.append("var newInput = document.createElement('input');");
1028         context.prettyLine();
1029         context.append("newInput.setAttribute('type','hidden');");
1030         context.prettyLine();
1031         context.append("newInput.setAttribute('id',name);");  // IE hack; See MYFACES-1805
1032         context.prettyLine();
1033         context.append("newInput.setAttribute('name',name);");
1034         context.prettyLine();
1035         context.append("newInput.setAttribute('value',value);");
1036         context.prettyLine();
1037         context.append("form.appendChild(newInput);");
1038         context.append("}");
1039 
1040         context.append("}");
1041 
1042         context.prettyLine();
1043 
1044         context.prettyLine();
1045 
1046         //render a function to clear a hidden input, if it exists        
1047         context.append("function ");
1048         context.append(CLEAR_HIDDEN_INPUT_FN_NAME).append("(formname, name, value)");
1049         context.append("{");
1050         context.append("var form = document.forms[formname];");
1051         context.prettyLine();
1052         context.append("if (typeof form == 'undefined')");
1053         context.append("{");
1054         context.append("form = document.getElementById(formname);");
1055         context.append("}");
1056         context.prettyLine();
1057         context.append("var hInput = form.elements[name];");
1058         context.prettyLine();
1059         context.append("if(typeof hInput !='undefined')");
1060         context.append("{");
1061         //context.append("form.elements[name].value=null;");
1062         context.append("form.removeChild(hInput);");
1063         context.append("}");
1064 
1065         context.append("}");
1066 
1067         context.prettyLine();
1068 
1069         context.append("function ");
1070         context.append(SUBMIT_FORM_FN_NAME).append("(formName, linkId, target, params)");
1071         context.append("{");
1072 
1073         //call the script to clear the form (clearFormHiddenParams_<formName>) method - optionally, only necessary for IE5.5.
1074         //todo: if IE5.5. is ever desupported, we can get rid of this and instead rely on the last part of this script to
1075         //clear the parameters
1076         HtmlRendererUtils.appendClearHiddenCommandFormParamsFunctionCall(context, null);
1077 
1078         if (autoScroll)
1079         {
1080             appendAutoScrollAssignment(context, null);
1081         }
1082 
1083         context.prettyLine();
1084 
1085         context.append("var form = document.forms[formName];");
1086         context.prettyLine();
1087         context.append("if (typeof form == 'undefined')");
1088         context.append("{");
1089         context.append("form = document.getElementById(formName);");
1090         context.append("}");
1091         context.prettyLine();
1092         
1093         if (JavascriptUtils.isSaveFormSubmitLinkIE(FacesContext.getCurrentInstance().getExternalContext())){
1094             context.append("var agentString = navigator.userAgent.toLowerCase();");
1095             context.prettyLine();
1096             //context.append("var isIE = false;");
1097             context.prettyLine();
1098             context.append("if (agentString.indexOf('msie') != -1)");
1099         
1100             context.append("{");
1101             context.append("if (!(agentString.indexOf('ppc') != -1 && agentString.indexOf('windows ce') != -1 && version >= 4.0))");              
1102             context.append("{");
1103             context.append("window.external.AutoCompleteSaveForm(form);");        
1104 //        context.append("isIE = false;");
1105             context.append("}");
1106 //        context.append("else");
1107 //        context.append("{");
1108 //        context.append("isIE = true;");
1109 //        context.prettyLine();
1110 //        context.append("}");
1111         
1112             context.append("}");
1113 
1114             context.prettyLine();
1115         }
1116         //set the target (and save it). This should be done always, 
1117         //and the default value of target is always valid.
1118         context.append("var oldTarget = form.target;");
1119         context.prettyLine();
1120         context.append("if(target != null)");
1121         context.append("{");
1122         context.prettyLine();
1123         context.append("form.target=target;");
1124         context.append("}");
1125 
1126         //set the submit parameters
1127 
1128         context.append("if((typeof params!='undefined') && params != null)");
1129         context.append("{");
1130         context.prettyLine();
1131         context.append("for(var i=0, param; (param = params[i]); i++)");
1132         context.append("{");
1133         context.append(SET_HIDDEN_INPUT_FN_NAME).append("(formName,param[0], param[1]);");
1134         context.append("}");
1135         context.append("}");
1136 
1137         context.prettyLine();
1138 
1139         context.append(SET_HIDDEN_INPUT_FN_NAME);
1140         context.append("(formName,formName +'"+NamingContainer.SEPARATOR_CHAR+
1141                 "'+'"+HtmlRendererUtils.HIDDEN_COMMANDLINK_FIELD_NAME+"',linkId);");
1142 
1143         context.prettyLine();
1144         context.prettyLine();
1145 
1146         //do the actual submit calls
1147 
1148         context.append("if(form.onsubmit)");
1149         context.append("{");
1150         context.append("var result=form.onsubmit();");
1151         context.prettyLine();
1152         context.append("if((typeof result=='undefined')||result)");
1153         context.append("{");
1154         context.append("try");
1155         context.append("{");
1156         context.append("form.submit();");
1157         context.append("}");
1158         context.append("catch(e){}");
1159         context.append("}");
1160         context.append("}");
1161         context.append("else ");
1162         context.append("{");
1163         context.append("try");
1164         context.append("{");        
1165         context.append("form.submit();");
1166         context.append("}");
1167         context.append("catch(e){}");        
1168         context.append("}");
1169 
1170         //reset the target
1171         context.prettyLine();
1172         //Restore the old target, no more questions asked
1173         context.append("form.target=oldTarget;");
1174         context.prettyLine();
1175 
1176         //clear the individual parameters - to make sure that even if the clear-function isn't called,
1177         // the back button/resubmit functionality will still work in all browsers except IE 5.5.
1178 
1179         context.append("if((typeof params!='undefined') && params != null)");
1180         context.append("{");
1181         context.prettyLine();
1182         context.append("for(var i=0, param; (param = params[i]); i++)");        
1183         context.append("{");
1184         context.append(CLEAR_HIDDEN_INPUT_FN_NAME).append("(formName,param[0], param[1]);");
1185         context.append("}");
1186         context.append("}");
1187 
1188         context.prettyLine();
1189 
1190         context.append(CLEAR_HIDDEN_INPUT_FN_NAME);
1191         context.append("(formName,formName +'"+NamingContainer.SEPARATOR_CHAR+
1192                 "'+'"+HtmlRendererUtils.HIDDEN_COMMANDLINK_FIELD_NAME+"',linkId);");
1193 
1194 
1195         //return false, so that browser does not handle the click
1196         context.append("return false;");
1197         context.append("}");
1198 
1199         context.prettyLineDecreaseIndent();
1200     }
1201 
1202     /***
1203      * Adds the hidden form input value assignment that is necessary for the autoscroll
1204      * feature to an html link or button onclick attribute.
1205      */
1206     public static void appendAutoScrollAssignment(StringBuffer onClickValue, String formName)
1207     {
1208         appendAutoScrollAssignment(new ScriptContext(onClickValue,false),formName);
1209     }
1210 
1211     private static void appendAutoScrollAssignment(ScriptContext scriptContext, String formName)
1212     {
1213         String formNameStr = formName == null? "formName" : (new StringBuffer("'").append(formName).append("'").toString());
1214         String paramName = new StringBuffer().append("'").
1215                 append(AUTO_SCROLL_PARAM).append("'").toString();
1216         String value = new StringBuffer().append(AUTO_SCROLL_FUNCTION).append("()").toString();
1217 
1218         scriptContext.prettyLine();
1219         scriptContext.append("if(typeof window."+AUTO_SCROLL_FUNCTION+"!='undefined')");
1220         scriptContext.append("{");
1221         scriptContext.append(SET_HIDDEN_INPUT_FN_NAME);
1222         scriptContext.append("(").append(formNameStr).append(",").append(paramName).append(",").append(value).append(");");
1223         scriptContext.append("}");
1224 
1225     }
1226 
1227     /***
1228      * Renders the hidden form input that is necessary for the autoscroll feature.
1229      */
1230     public static void renderAutoScrollHiddenInput(FacesContext facesContext, ResponseWriter writer) throws IOException
1231     {
1232         writePrettyLineSeparator(facesContext);
1233         writer.startElement(HTML.INPUT_ELEM, null);
1234         writer.writeAttribute(HTML.TYPE_ATTR, "hidden", null);
1235         writer.writeAttribute(HTML.NAME_ATTR, AUTO_SCROLL_PARAM, null);
1236         writer.endElement(HTML.INPUT_ELEM);
1237         writePrettyLineSeparator(facesContext);
1238     }
1239 
1240     /***
1241      * Renders the autoscroll javascript function.
1242      */
1243     public static void renderAutoScrollFunction(FacesContext facesContext,
1244                                                 ResponseWriter writer) throws IOException
1245     {
1246         writePrettyLineSeparator(facesContext);
1247         writer.startElement(HTML.SCRIPT_ELEM,null);
1248         writer.writeAttribute(HTML.SCRIPT_TYPE_ATTR, HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT,null);
1249 
1250         ScriptContext script = new ScriptContext(
1251                 MyfacesConfig.getCurrentInstance(facesContext.getExternalContext()).isPrettyHtml());
1252 
1253         script.prettyLineIncreaseIndent();
1254 
1255         script.append("function ");
1256         script.append(AUTO_SCROLL_FUNCTION);
1257         script.append("()");
1258         script.append("{");
1259         script.append("var x = 0; var y = 0;");
1260         script.append( "if (self.pageXOffset || self.pageYOffset)");
1261         script.append("{");
1262         script.append("x = self.pageXOffset;");
1263         script.prettyLine();
1264         script.append("y = self.pageYOffset;");
1265         script.append("}");
1266         script.append(" else if ((document.documentElement && document.documentElement.scrollLeft)||(document.documentElement && document.documentElement.scrollTop))");
1267         script.append("{");
1268         script.append("x = document.documentElement.scrollLeft;");
1269         script.prettyLine();
1270         script.append("y = document.documentElement.scrollTop;");
1271         script.append("}");
1272         script.append(" else if (document.body) ");
1273         script.append("{");
1274         script.append("x = document.body.scrollLeft;");
1275         script.prettyLine();
1276         script.append("y = document.body.scrollTop;");
1277         script.append("}");
1278         script.append("return x + \",\" + y;");
1279         script.append("}");
1280 
1281         ExternalContext externalContext = facesContext.getExternalContext();
1282         String oldViewId = JavascriptUtils.getOldViewId(externalContext);
1283         if (oldViewId != null && oldViewId.equals(facesContext.getViewRoot().getViewId()))
1284         {
1285             //ok, we stayed on the same page, so let's scroll it to the former place
1286             String scrolling = (String)externalContext.getRequestParameterMap().get(AUTO_SCROLL_PARAM);
1287             if (scrolling != null && scrolling.length() > 0)
1288             {
1289                 int x = 0;
1290                 int y = 0;
1291                 int comma = scrolling.indexOf(',');
1292                 if (comma == -1)
1293                 {
1294                     log.warn("Illegal autoscroll request parameter: " + scrolling);
1295                 }
1296                 else
1297                 {
1298                     try {
1299                         //we convert to int against XSS vulnerability
1300                         x = Integer.parseInt(scrolling.substring(0, comma));
1301                     } catch (NumberFormatException e) {
1302                         log.warn("Error getting x offset for autoscroll feature. Bad param value: " + scrolling);
1303                         x = 0; //ignore false numbers
1304                     }
1305 
1306                     try {
1307                         //we convert to int against XSS vulnerability
1308                         y = Integer.parseInt(scrolling.substring(comma + 1));
1309                     } catch (NumberFormatException e) {
1310                         log.warn("Error getting y offset for autoscroll feature. Bad param value: " + scrolling);
1311                         y = 0; //ignore false numbers
1312                     }
1313                 }
1314                 script.append("window.scrollTo(").append(x).append(",").append(y).append(");\n");
1315             }
1316         }
1317 
1318         writer.writeText(script.toString(),null);
1319 
1320         writer.endElement(HTML.SCRIPT_ELEM);
1321         writePrettyLineSeparator(facesContext);
1322     }
1323 
1324     public static boolean isAllowedCdataSection(FacesContext fc) {
1325         Boolean value = null;
1326 
1327         if (fc != null) {
1328             value = (Boolean) fc.getExternalContext().getRequestMap().get(ALLOW_CDATA_SECTION_ON);
1329         }
1330 
1331         return value != null && ((Boolean) value).booleanValue();
1332     }
1333 
1334     public static void allowCdataSection(FacesContext fc, boolean cdataSectionAllowed)
1335     {
1336           fc.getExternalContext().getRequestMap().put(ALLOW_CDATA_SECTION_ON,Boolean.valueOf(cdataSectionAllowed));
1337     }
1338 
1339     public static class LinkParameter {
1340         private String _name;
1341 
1342         private Object _value;
1343 
1344         public String getName() {
1345             return _name;
1346         }
1347 
1348         public void setName(String name) {
1349             _name = name;
1350         }
1351 
1352         public Object getValue() {
1353             return _value;
1354         }
1355 
1356         public void setValue(Object value) {
1357             _value = value;
1358         }
1359 
1360     }
1361 
1362     public static void renderHiddenCommandFormParams(ResponseWriter writer,
1363                                                      Set dummyFormParams) throws IOException {
1364         for (Iterator it = dummyFormParams.iterator(); it.hasNext();) {
1365             Object name = it.next();
1366             renderHiddenInputField(writer, name, null);
1367         }
1368     }
1369 
1370     public static void renderHiddenInputField(ResponseWriter writer, Object name, Object value)
1371             throws IOException
1372     {
1373         writer.startElement(HTML.INPUT_ELEM, null);
1374         writer.writeAttribute(HTML.TYPE_ATTR, HTML.INPUT_TYPE_HIDDEN, null);
1375         writer.writeAttribute(HTML.NAME_ATTR, name, null);
1376         if(value!=null)
1377         {
1378             writer.writeAttribute(HTML.VALUE_ATTR,value,null);
1379         }
1380         writer.endElement(HTML.INPUT_ELEM);
1381     }
1382 
1383     /***
1384      * @deprecated Replaced by 
1385      * renderLabel(ResponseWriter writer,
1386                                    UIComponent component,
1387                                    String forClientId,
1388                                    SelectItem item,
1389                                    boolean disabled).
1390      * Renders a label HTML element
1391      */
1392     @Deprecated
1393     public static void renderLabel(ResponseWriter writer,
1394                                    UIComponent component,
1395                                    String forClientId,
1396                                    String labelValue,
1397                                    boolean disabled) throws IOException
1398     {
1399             writer.startElement(HTML.LABEL_ELEM, component);
1400             writer.writeAttribute(HTML.FOR_ATTR, forClientId, null);
1401 
1402             String labelClass = null;
1403 
1404             if (disabled)
1405             {
1406                 labelClass = (String) component.getAttributes().get(JSFAttr.DISABLED_CLASS_ATTR);
1407             }
1408             else
1409             {
1410                 labelClass = (String) component.getAttributes().get(org.apache.myfaces.shared.renderkit.JSFAttr.ENABLED_CLASS_ATTR);
1411             }
1412             if (labelClass != null)
1413             {
1414                 writer.writeAttribute("class", labelClass, "labelClass");
1415             }
1416 
1417             if ((labelValue != null) && (labelValue.length() > 0))
1418             {
1419                 writer.write(HTML.NBSP_ENTITY);
1420                 writer.writeText(labelValue, null);
1421             }
1422 
1423             writer.endElement(HTML.LABEL_ELEM);
1424     }
1425     
1426     /***
1427      * Renders a label HTML element
1428      */
1429     public static void renderLabel(ResponseWriter writer,
1430                                    UIComponent component,
1431                                    String forClientId,
1432                                    SelectItem item,
1433                                    boolean disabled) throws IOException
1434     {
1435             writer.startElement(HTML.LABEL_ELEM, component);
1436             writer.writeAttribute(HTML.FOR_ATTR, forClientId, null);
1437 
1438             String labelClass = null;
1439 
1440             if (disabled)
1441             {
1442                 labelClass = (String) component.getAttributes().get(JSFAttr.DISABLED_CLASS_ATTR);
1443             }
1444             else
1445             {
1446                 labelClass = (String) component.getAttributes().get(org.apache.myfaces.shared.renderkit.JSFAttr.ENABLED_CLASS_ATTR);
1447             }
1448             if (labelClass != null)
1449             {
1450                 writer.writeAttribute("class", labelClass, "labelClass");
1451             }
1452 
1453             if ((item.getLabel() != null) && (item.getLabel().length() > 0))
1454             {
1455                 writer.write(HTML.NBSP_ENTITY);
1456                 if(item.isEscape())
1457                 {
1458                     writer.writeText(item.getLabel(), null);
1459                 }
1460                 else
1461                 {
1462                     writer.write(item.getLabel());
1463                 }
1464             }
1465 
1466             writer.endElement(HTML.LABEL_ELEM);
1467     }
1468 
1469     /***
1470      * Render the javascript function that is called on a click on a commandLink
1471      * to clear the hidden inputs. This is necessary because on a browser back,
1472      * each hidden input still has it's old value (browser cache!) and therefore
1473      * a new submit would cause the according action once more!
1474      *
1475      * @param writer
1476      * @param formName
1477      * @param dummyFormParams
1478      * @param formTarget
1479      * @throws IOException
1480      */
1481     public static void renderClearHiddenCommandFormParamsFunction(
1482             ResponseWriter writer, String formName, Set dummyFormParams,
1483             String formTarget) throws IOException {
1484         //render the clear hidden inputs javascript function
1485         String functionName = getClearHiddenCommandFormParamsFunctionName(formName);
1486         writer.startElement(HTML.SCRIPT_ELEM, null);
1487         writer.writeAttribute(HTML.TYPE_ATTR, "text/javascript", null);
1488 
1489         // Using writeComment instead of write with <!-- tag
1490         StringBuffer script = new StringBuffer();
1491         script.append("function ");
1492         script.append(functionName);
1493         script.append("() {");
1494         if (dummyFormParams != null) {
1495             script.append("\n  var f = document.forms['");
1496             script.append(formName);
1497             script.append("'];");
1498             int i=0;
1499             for (Iterator it = dummyFormParams.iterator(); it.hasNext();) {
1500                 String elemVarName = "elem"+i;
1501                 script.append("\n  var ").append(elemVarName).append(" = ");
1502                 script.append("f.elements['").append((String) it.next()).append("'];");
1503                 script.append("\n  if(typeof ").append(elemVarName).append(" !='undefined' && ");
1504                 script.append(elemVarName).append(".nodeName=='INPUT'){");
1505                 script.append("\n   if (").append(elemVarName).append(".value != '') {");
1506                 script.append("\n    "+elemVarName+".value='';");
1507                 script.append("\n   }");
1508                 script.append("\n  }");
1509                 i++;
1510             }
1511         }
1512         // clear form target
1513         script.append("\n  f.target=");
1514         if (formTarget == null || formTarget.length() == 0) {
1515             //Normally one would think that setting target to null has the
1516             //desired effect, but once again IE is different...
1517             //Setting target to null causes IE to open a new window!
1518             script.append("'';");
1519         } else {
1520             script.append("'");
1521             script.append(formTarget);
1522             script.append("';");
1523         }
1524         script.append("\n}");
1525 
1526         //Just to be sure we call this clear method on each load.
1527         //Otherwise in the case, that someone submits a form by pressing Enter
1528         //within a text input, the hidden inputs won't be cleared!
1529         script.append("\n");
1530         script.append(functionName);
1531         script.append("();");
1532 
1533         writer.writeText(script.toString(),null);
1534         writer.endElement(HTML.SCRIPT_ELEM);
1535     }
1536 
1537     /***
1538      * Prefixes the given String with "clear_" and removes special characters
1539      *
1540      * @param formName
1541      * @return String
1542      */
1543     public static String getClearHiddenCommandFormParamsFunctionName(
1544             String formName)
1545     {
1546         if (formName == null)
1547         {
1548             return "'" + CLEAR_HIDDEN_FIELD_FN_NAME
1549                     + "_'+formName.replace(/-/g, '//$" + NamingContainer.SEPARATOR_CHAR + "').replace(/" + NamingContainer.SEPARATOR_CHAR + "/g,'_')";
1550         }
1551 
1552         return JavascriptUtils.getValidJavascriptNameAsInRI(CLEAR_HIDDEN_FIELD_FN_NAME
1553                 + "_"
1554                 + formName
1555                 .replace(NamingContainer.SEPARATOR_CHAR, '_'));
1556     }
1557 
1558     public static String getClearHiddenCommandFormParamsFunctionNameMyfacesLegacy(
1559             String formName)
1560     {
1561         return "clear_"
1562                 + JavascriptUtils.getValidJavascriptName(formName, false);
1563     }
1564 
1565 
1566     /***
1567      * Get the name of the request parameter that holds the id of the
1568      * link-type component that caused the form to be submitted.
1569      * <p/>
1570      * Within each page there may be multiple "link" type components that
1571      * cause page submission. On the server it is necessary to know which
1572      * of these actually caused the submit, in order to invoke the correct
1573      * listeners. Such components therefore store their id into the
1574      * "hidden command link field" in their associated form before
1575      * submitting it.
1576      * <p/>
1577      * The field is always a direct child of each form, and has the same
1578      * <i>name</i> in each form. The id of the form component is therefore
1579      * both necessary and sufficient to determine the full name of the
1580      * field.
1581      */
1582     public static String getHiddenCommandLinkFieldName(FormInfo formInfo) {
1583         if (RendererUtils.isAdfOrTrinidadForm(formInfo.getForm())) {
1584             return HIDDEN_COMMANDLINK_FIELD_NAME_TRINIDAD;
1585         }
1586         return formInfo.getFormName() + NamingContainer.SEPARATOR_CHAR
1587             + HIDDEN_COMMANDLINK_FIELD_NAME;
1588     }
1589 
1590     /***
1591      * 
1592      * @param formInfo
1593      * @deprecated Use getHiddenCommandLinkFieldNameMyfaces(FormInfo) instead
1594      * @return
1595      */
1596     public static String getHiddenCommandLinkFieldNameMyfacesOld(FormInfo formInfo) {
1597         return formInfo.getFormName() + NamingContainer.SEPARATOR_CHAR
1598             + HIDDEN_COMMANDLINK_FIELD_NAME_MYFACES_OLD;
1599     }
1600 
1601 
1602     private static String HTML_CONTENT_TYPE = "text/html";
1603     private static String TEXT_ANY_CONTENT_TYPE = "text/*";
1604     private static String ANY_CONTENT_TYPE = "*/*";
1605 
1606     public static String DEFAULT_CHAR_ENCODING = "ISO-8859-1";
1607     private static String XHTML_CONTENT_TYPE = "application/xhtml+xml";
1608     private static String APPLICATION_XML_CONTENT_TYPE = "application/xml";
1609     private static String TEXT_XML_CONTENT_TYPE = "text/xml";
1610 
1611     // The order is important in this case.
1612     private static final String [] SUPPORTED_CONTENT_TYPES = {
1613             HTML_CONTENT_TYPE, //Prefer this over any other, because IE does not support XHTML content type
1614             XHTML_CONTENT_TYPE,
1615             APPLICATION_XML_CONTENT_TYPE,
1616             TEXT_XML_CONTENT_TYPE,
1617             TEXT_ANY_CONTENT_TYPE,
1618             ANY_CONTENT_TYPE
1619         };
1620 
1621     public static String selectContentType(String contentTypeListString)
1622     {
1623         if (contentTypeListString == null)
1624         {
1625             FacesContext context = FacesContext.getCurrentInstance();
1626             if(context != null)
1627             {
1628                 contentTypeListString = (String)
1629                     context.getExternalContext().getRequestHeaderMap().get("Accept");
1630                 
1631                 // There is a windows mobile IE client (6.12) sending
1632                 // "application/vnd.wap.mms-message;*/*"
1633                 // Note that the Accept header should be written as 
1634                 // "application/vnd.wap.mms-message,*/*" ,
1635                 // so this is bug of the client. Anyway, this is a workaround ...
1636                 if (contentTypeListString != null && 
1637                     contentTypeListString.startsWith("application/vnd.wap.mms-message;*/*"))
1638                 {
1639                         contentTypeListString = "*/*";
1640                 }
1641             }
1642 
1643             if(contentTypeListString == null)
1644             {
1645                 if (log.isDebugEnabled())
1646                     log.debug("No content type list given, creating HtmlResponseWriterImpl with default content type.");
1647 
1648                 contentTypeListString = HTML_CONTENT_TYPE;
1649             }
1650         }
1651 
1652         List contentTypeList = splitContentTypeListString(contentTypeListString);
1653         String[] supportedContentTypeArray = getSupportedContentTypes();
1654 
1655         String selectedContentType = null;
1656 
1657         for (int i = 0; i < supportedContentTypeArray.length; i++)
1658         {
1659             String supportedContentType = supportedContentTypeArray[i].trim();
1660 
1661             for (int j = 0; j < contentTypeList.size(); j++)
1662             {
1663                 String contentType = (String) contentTypeList.get(j);
1664 
1665                 if (contentType.indexOf(supportedContentType) != -1)
1666                 {
1667                     if (isHTMLContentType(contentType)) {
1668                         selectedContentType = HTML_CONTENT_TYPE;
1669                     }
1670 
1671                     else if (isXHTMLContentType(contentType)) {
1672                         selectedContentType = XHTML_CONTENT_TYPE;
1673                     }
1674                     break;
1675                 }
1676             }
1677             if (selectedContentType!=null)
1678             {
1679                 break;
1680             }
1681         }
1682 
1683         if(selectedContentType==null)
1684         {
1685             throw new IllegalArgumentException("ContentTypeList does not contain a supported content type: " +
1686                                                contentTypeListString);
1687         }
1688         return selectedContentType;
1689     }
1690 
1691     public static String[] getSupportedContentTypes()
1692     {
1693         //String[] supportedContentTypeArray = new String[]{HTML_CONTENT_TYPE,TEXT_ANY_CONTENT_TYPE,ANY_CONTENT_TYPE,
1694         //                                                  XHTML_CONTENT_TYPE,APPLICATION_XML_CONTENT_TYPE,TEXT_XML_CONTENT_TYPE};
1695         return SUPPORTED_CONTENT_TYPES;
1696     }
1697 
1698     private static boolean isHTMLContentType(String contentType)
1699     {
1700         return contentType.indexOf(HTML_CONTENT_TYPE) != -1 ||
1701             contentType.indexOf(ANY_CONTENT_TYPE) != -1 ||
1702             contentType.indexOf(TEXT_ANY_CONTENT_TYPE) != -1 ;
1703     }
1704 
1705     public static boolean isXHTMLContentType(String contentType)
1706     {
1707         return contentType.indexOf(XHTML_CONTENT_TYPE) != -1 ||
1708                contentType.indexOf(APPLICATION_XML_CONTENT_TYPE) != -1 ||
1709                contentType.indexOf(TEXT_XML_CONTENT_TYPE) != -1;
1710     }
1711 
1712     private static List splitContentTypeListString(String contentTypeListString)
1713     {
1714         List contentTypeList = new ArrayList();
1715 
1716         StringTokenizer st = new StringTokenizer(contentTypeListString, ",");
1717         while (st.hasMoreTokens())
1718         {
1719             String contentType = st.nextToken().trim();
1720 
1721             int semicolonIndex = contentType.indexOf(";");
1722 
1723             if (semicolonIndex!=-1)
1724             {
1725                 contentType = contentType.substring(0,semicolonIndex);
1726             }
1727 
1728             contentTypeList.add(contentType);
1729         }
1730 
1731         return contentTypeList;
1732     }
1733 
1734     public static String getJavascriptLocation(UIComponent component)
1735     {
1736         if(component==null)
1737             return null;
1738 
1739         return (String)component.getAttributes().get(JSFAttr.JAVASCRIPT_LOCATION);
1740     }
1741 
1742     public static String getImageLocation(UIComponent component)
1743     {
1744         if(component==null)
1745             return null;
1746 
1747         return (String)component.getAttributes().get(JSFAttr.IMAGE_LOCATION);
1748     }
1749 
1750     public static String getStyleLocation(UIComponent component)
1751     {
1752         if(component==null)
1753             return null;
1754 
1755         return (String)component.getAttributes().get(JSFAttr.STYLE_LOCATION);
1756     }
1757 
1758     public static void renderViewStateJavascript(FacesContext facesContext, String hiddenId, String serializedState) throws IOException {
1759         ResponseWriter writer = facesContext.getResponseWriter();
1760 
1761         writer.startElement(HTML.SCRIPT_ELEM, null);
1762         writer.writeAttribute(HTML.TYPE_ATTR, "text/javascript", null);
1763 
1764         final ExternalContext externalContext = facesContext.getExternalContext();
1765         final MyfacesConfig currentInstance = MyfacesConfig.getCurrentInstance(externalContext);
1766 
1767         ScriptContext context = new ScriptContext(currentInstance.isPrettyHtml());
1768         context.prettyLine();
1769         context.increaseIndent();
1770 
1771         context.append("function setViewState() {\n");
1772         context.append("\tvar state = '");
1773         context.append(serializedState);
1774         context.append("';\n");
1775         context.append("\tfor (var i = 0; i < document.forms.length; i++) {\n");
1776         context.append("\t\tdocument.forms[i]['" + hiddenId + "'].value = state;\n");
1777         context.append("\t}\n");
1778         context.append("}\n");
1779         context.append("setViewState();\n");
1780 
1781         context.decreaseIndent();
1782 
1783         writer.writeText(context.toString(), null);
1784 
1785         writer.endElement(HTML.SCRIPT_ELEM);
1786     }
1787     /***
1788      * The ScriptContext offers methods and fields
1789      * to help with rendering out a script and keeping a
1790      * proper formatting.
1791      */
1792     public static class ScriptContext
1793     {
1794         private long currentIndentationLevel;
1795         private StringBuffer buffer = new StringBuffer();
1796         private boolean prettyPrint = false;
1797         /***
1798          *  automatic formatting will render
1799          *  new-lines and indents if blocks are opened
1800          *  and closed - attention: you need to append
1801          *  opening and closing brackets of blocks separately in this case!
1802          */
1803         private boolean automaticFormatting = true;
1804 
1805         public ScriptContext()
1806         {
1807 
1808         }
1809 
1810         public ScriptContext(boolean prettyPrint)
1811         {
1812             this.prettyPrint = prettyPrint;
1813         }
1814 
1815         public ScriptContext(StringBuffer buf, boolean prettyPrint)
1816         {
1817             this.prettyPrint = prettyPrint;
1818             this.buffer = buf;
1819         }
1820 
1821         public void increaseIndent()
1822         {
1823             currentIndentationLevel++;
1824         }
1825 
1826         public void decreaseIndent()
1827         {
1828             currentIndentationLevel--;
1829 
1830             if(currentIndentationLevel<0)
1831                 currentIndentationLevel=0;
1832         }
1833 
1834         public void prettyLine()
1835         {
1836             if(prettyPrint)
1837             {
1838                 append(LINE_SEPARATOR);
1839 
1840                 for(int i=0; i<getCurrentIndentationLevel(); i++)
1841                     append(TABULATOR);
1842             }
1843         }
1844 
1845         public void prettyLineIncreaseIndent()
1846         {
1847             increaseIndent();
1848             prettyLine();
1849         }
1850 
1851         public void prettyLineDecreaseIndent()
1852         {
1853             decreaseIndent();
1854             prettyLine();
1855         }
1856 
1857         public long getCurrentIndentationLevel()
1858         {
1859             return currentIndentationLevel;
1860         }
1861 
1862         public void setCurrentIndentationLevel(long currentIndentationLevel)
1863         {
1864             this.currentIndentationLevel = currentIndentationLevel;
1865         }
1866 
1867         public ScriptContext append(String str)
1868         {
1869 
1870             if(automaticFormatting && str.length()==1)
1871             {
1872                 boolean openBlock = str.equals("{");
1873                 boolean closeBlock = str.equals("}");
1874 
1875                 if(openBlock)
1876                 {
1877                     prettyLine();
1878                 }
1879                 else if(closeBlock)
1880                 {
1881                     prettyLineDecreaseIndent();
1882                 }
1883 
1884                 buffer.append(str);
1885 
1886                 if(openBlock)
1887                 {
1888                     prettyLineIncreaseIndent();
1889                 }
1890                 else if(closeBlock)
1891                 {
1892                     prettyLine();
1893                 }
1894             }
1895             else
1896             {
1897                 buffer.append(str);
1898             }
1899             return this;
1900         }
1901 
1902         public ScriptContext append(char c)
1903         {
1904             buffer.append(c);
1905             return this;
1906         }
1907 
1908         public ScriptContext append(int i) {
1909             buffer.append(i);
1910             return this;
1911         }
1912 
1913         public String toString()
1914         {
1915             return buffer.toString();
1916         }
1917     }
1918 }