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