View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.shared.renderkit.html;
20  
21  import java.io.IOException;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.logging.Level;
25  import java.util.logging.Logger;
26  
27  import javax.faces.component.UIComponent;
28  import javax.faces.component.UIInput;
29  import javax.faces.component.UIOutput;
30  import javax.faces.component.UIViewRoot;
31  import javax.faces.component.behavior.ClientBehavior;
32  import javax.faces.component.behavior.ClientBehaviorHolder;
33  import javax.faces.component.html.HtmlInputText;
34  import javax.faces.component.html.HtmlOutputText;
35  import javax.faces.context.FacesContext;
36  import javax.faces.context.ResponseWriter;
37  import javax.faces.convert.ConverterException;
38  
39  import org.apache.myfaces.shared.component.EscapeCapable;
40  import org.apache.myfaces.shared.renderkit.JSFAttr;
41  import org.apache.myfaces.shared.renderkit.RendererUtils;
42  import org.apache.myfaces.shared.renderkit.html.util.ResourceUtils;
43  
44  public class HtmlTextRendererBase
45          extends HtmlRenderer
46  {
47      //private static final Log log = LogFactory.getLog(HtmlTextRendererBase.class);
48      private static final Logger log = Logger.getLogger(HtmlTextRendererBase.class.getName());
49  
50      private static final String AUTOCOMPLETE_VALUE_OFF = "off";
51  
52      public void encodeEnd(FacesContext facesContext, UIComponent component)
53          throws IOException
54      {
55          org.apache.myfaces.shared.renderkit.RendererUtils.checkParamValidity(facesContext,component,null);
56          
57          Map<String, List<ClientBehavior>> behaviors = null;
58          if (component instanceof ClientBehaviorHolder)
59          {
60              behaviors = ((ClientBehaviorHolder) component).getClientBehaviors();
61              if (!behaviors.isEmpty())
62              {
63                  ResourceUtils.renderDefaultJsfJsInlineIfNecessary(facesContext, facesContext.getResponseWriter());
64              }
65          }
66          
67          if (component instanceof UIInput)
68          {
69              renderInput(facesContext, component);
70          }
71          else if (component instanceof UIOutput)
72          {
73              renderOutput(facesContext, component);
74          }
75          else
76          {
77              throw new IllegalArgumentException("Unsupported component class " + component.getClass().getName());
78          }
79      }
80  
81  
82      protected void renderOutput(FacesContext facesContext, UIComponent component)
83          throws IOException
84      {
85          
86          String text = org.apache.myfaces.shared.renderkit.RendererUtils.getStringValue(facesContext, component);
87          if (log.isLoggable(Level.FINE))
88          {
89              log.fine("renderOutput '" + text + "'");
90          }
91          boolean escape;
92          if (component instanceof HtmlOutputText || component instanceof EscapeCapable)
93          {
94              escape = ((HtmlOutputText)component).isEscape();
95          }
96          else
97          {
98              escape = RendererUtils.getBooleanAttribute(component, 
99                      org.apache.myfaces.shared.renderkit.JSFAttr.ESCAPE_ATTR,
100                                                        true); //default is to escape
101         }
102         if (text != null)
103         {
104             ResponseWriter writer = facesContext.getResponseWriter();
105             boolean span = false;
106 
107             if (isCommonPropertiesOptimizationEnabled(facesContext))
108             {
109                 long commonPropertiesMarked = CommonPropertyUtils.getCommonPropertiesMarked(component);
110                 if ( (commonPropertiesMarked & ~(CommonPropertyConstants.ESCAPE_PROP)) > 0)
111                 {
112                     span = true;
113                     writer.startElement(HTML.SPAN_ELEM, component);
114                     HtmlRendererUtils.writeIdIfNecessary(writer, component, facesContext);
115                 }
116                 else if (CommonPropertyUtils.isIdRenderingNecessary(component))
117                 {
118                     span = true;
119                     writer.startElement(HTML.SPAN_ELEM, component);
120                     writer.writeAttribute(HTML.ID_ATTR, component.getClientId(facesContext), null);
121                 }
122                 
123                 CommonPropertyUtils.renderUniversalProperties(writer, commonPropertiesMarked, component);
124                 CommonPropertyUtils.renderStyleProperties(writer, commonPropertiesMarked, component);
125                 
126                 if (isRenderOutputEventAttributes())
127                 {
128                     HtmlRendererUtils.renderHTMLAttributes(writer, component, HTML.EVENT_HANDLER_ATTRIBUTES);
129                 }
130             }
131             else
132             {
133                 if(component.getId()!=null && !component.getId().startsWith(UIViewRoot.UNIQUE_ID_PREFIX))
134                 {
135                     span = true;
136     
137                     writer.startElement(HTML.SPAN_ELEM, component);
138     
139                     HtmlRendererUtils.writeIdIfNecessary(writer, component, facesContext);
140     
141                     HtmlRendererUtils.renderHTMLAttributes(writer, component, HTML.COMMON_PASSTROUGH_ATTRIBUTES);
142     
143                 }
144                 else
145                 {
146                     span = HtmlRendererUtils.renderHTMLAttributesWithOptionalStartElement(writer,component,
147                             HTML.SPAN_ELEM,HTML.COMMON_PASSTROUGH_ATTRIBUTES);
148                 }
149             }
150 
151             if (escape)
152             {
153                 if (log.isLoggable(Level.FINE))
154                 {
155                     log.fine("renderOutputText writing '" + text + "'");
156                 }
157                 writer.writeText(text, org.apache.myfaces.shared.renderkit.JSFAttr.VALUE_ATTR);
158             }
159             else
160             {
161                 writer.write(text);
162             }
163 
164             if(span)
165             {
166                 writer.endElement(org.apache.myfaces.shared.renderkit.html.HTML.SPAN_ELEM);
167             }
168         }
169     }
170 
171     protected boolean isRenderOutputEventAttributes()
172     {
173         return true;
174     }
175 
176     protected void renderInput(FacesContext facesContext, UIComponent component)
177         throws IOException
178     {
179         //allow subclasses to render custom attributes by separating rendering begin and end 
180         renderInputBegin(facesContext, component);
181         renderInputEnd(facesContext, component);
182     }
183 
184     //Subclasses can set the value of an attribute before, or can render a custom attribute after calling this method
185     protected void renderInputBegin(FacesContext facesContext,
186             UIComponent component) throws IOException
187     {
188         ResponseWriter writer = facesContext.getResponseWriter();
189 
190         String clientId = component.getClientId(facesContext);
191 
192         writer.startElement(HTML.INPUT_ELEM, component);
193         writer.writeAttribute(HTML.ID_ATTR, clientId, null);
194         writer.writeAttribute(HTML.NAME_ATTR, clientId, null);
195         
196         //allow extending classes to modify html input element's type
197         String inputHtmlType = getInputHtmlType(component);
198         writer.writeAttribute(HTML.TYPE_ATTR, inputHtmlType, null);
199 
200         renderValue(facesContext, component, writer);
201 
202         Map<String, List<ClientBehavior>> behaviors = null;
203         if (component instanceof ClientBehaviorHolder)
204         {
205             behaviors = ((ClientBehaviorHolder) component).getClientBehaviors();
206             
207             long commonPropertiesMarked = 0L;
208             if (isCommonPropertiesOptimizationEnabled(facesContext))
209             {
210                 commonPropertiesMarked = CommonPropertyUtils.getCommonPropertiesMarked(component);
211             }
212             if (behaviors.isEmpty() && isCommonPropertiesOptimizationEnabled(facesContext))
213             {
214                 CommonPropertyUtils.renderChangeEventProperty(writer, 
215                         commonPropertiesMarked, component);
216                 CommonPropertyUtils.renderEventProperties(writer, 
217                         commonPropertiesMarked, component);
218                 CommonPropertyUtils.renderFieldEventPropertiesWithoutOnchange(writer, 
219                         commonPropertiesMarked, component);
220             }
221             else
222             {
223                 HtmlRendererUtils.renderBehaviorizedOnchangeEventHandler(facesContext, writer, component, behaviors);
224                 if (isCommonEventsOptimizationEnabled(facesContext))
225                 {
226                     Long commonEventsMarked = CommonEventUtils.getCommonEventsMarked(component);
227                     CommonEventUtils.renderBehaviorizedEventHandlers(facesContext, writer, 
228                             commonPropertiesMarked, commonEventsMarked, component, behaviors);
229                     CommonEventUtils.renderBehaviorizedFieldEventHandlersWithoutOnchange(
230                         facesContext, writer, commonPropertiesMarked, commonEventsMarked, component, behaviors);
231                 }
232                 else
233                 {
234                     HtmlRendererUtils.renderBehaviorizedEventHandlers(facesContext, writer, component, behaviors);
235                     HtmlRendererUtils.renderBehaviorizedFieldEventHandlersWithoutOnchange(
236                             facesContext, writer, component, behaviors);
237                 }
238             }
239             if (isCommonPropertiesOptimizationEnabled(facesContext))
240             {
241                 CommonPropertyUtils.renderInputPassthroughPropertiesWithoutDisabledAndEvents(writer, 
242                         commonPropertiesMarked, component);
243             }
244             else
245             {
246                 HtmlRendererUtils.renderHTMLAttributes(writer, component, 
247                         HTML.INPUT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED_AND_EVENTS);
248             }
249         }
250         else
251         {
252             if (isCommonPropertiesOptimizationEnabled(facesContext))
253             {
254                 CommonPropertyUtils.renderInputPassthroughPropertiesWithoutDisabled(writer, 
255                         CommonPropertyUtils.getCommonPropertiesMarked(component), component);
256             }
257             else
258             {
259                 HtmlRendererUtils.renderHTMLAttributes(writer, component, 
260                         HTML.INPUT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED);
261             }
262         }
263 
264         if (isDisabled(facesContext, component))
265         {
266             writer.writeAttribute(HTML.DISABLED_ATTR, Boolean.TRUE, null);
267         }
268 
269         if (isAutocompleteOff(facesContext, component))
270         {
271             writer.writeAttribute(HTML.AUTOCOMPLETE_ATTR, AUTOCOMPLETE_VALUE_OFF, HTML.AUTOCOMPLETE_ATTR);
272         }
273     }
274 
275     protected void renderValue(FacesContext facesContext, UIComponent component, ResponseWriter writer)
276             throws IOException
277     {
278         String value = RendererUtils.getStringValue(facesContext, component);
279         if (log.isLoggable(Level.FINE))
280         {
281            log.fine("renderInput '" + value + "'");
282         }
283 
284         if (value != null)
285         {
286             writer.writeAttribute(HTML.VALUE_ATTR, value, JSFAttr.VALUE_ATTR);
287         }
288     }
289 
290     protected void renderInputEnd(FacesContext facesContext, UIComponent component) throws IOException
291     {
292         ResponseWriter writer = facesContext.getResponseWriter(); 
293 
294         writer.endElement(HTML.INPUT_ELEM);
295     }
296 
297     protected boolean isDisabled(FacesContext facesContext, UIComponent component)
298     {
299         //TODO: overwrite in extended HtmlTextRenderer and check for enabledOnUserRole
300         if (component instanceof HtmlInputText)
301         {
302             return ((HtmlInputText)component).isDisabled();
303         }
304 
305         return org.apache.myfaces.shared.renderkit.RendererUtils.getBooleanAttribute(component, 
306                 HTML.DISABLED_ATTR, false);
307         
308     }
309 
310     /**
311      * If autocomplete is "on" or not set, do not render it
312      */
313     protected boolean isAutocompleteOff(FacesContext facesContext, UIComponent component)
314     {
315         if (component instanceof HtmlInputText)
316         {
317             String autocomplete = ((HtmlInputText)component).getAutocomplete();
318             if (autocomplete != null)
319             {
320                 return autocomplete.equals(AUTOCOMPLETE_VALUE_OFF);
321             }
322         }
323 
324         return false;
325     }
326 
327     protected boolean isValidLength(FacesContext facesContext, UIInput input)
328     {
329         if (input instanceof HtmlInputText)
330         {
331             int maxlength = ((HtmlInputText)input).getMaxlength();
332             if (maxlength >= 0)
333             {
334                 String clientId = input.getClientId(facesContext);
335                 String value = facesContext.getExternalContext().getRequestParameterMap().get(clientId);
336                 if (value != null && value.length() > maxlength)
337                 {
338                     return false;
339                 }
340             }
341             
342         }
343         return true;
344     }
345 
346     public void decode(FacesContext facesContext, UIComponent component)
347     {
348         RendererUtils.checkParamValidity(facesContext,component,null);
349 
350         if (component instanceof UIInput)
351         {
352             if (!isValidLength(facesContext, (UIInput)component))
353             {
354                 return;
355             }
356             HtmlRendererUtils.decodeUIInput(facesContext, component);
357             if (component instanceof ClientBehaviorHolder &&
358                     !HtmlRendererUtils.isDisabled(component))
359             {
360                 HtmlRendererUtils.decodeClientBehaviors(facesContext, component);
361             }
362         }
363         else if (component instanceof UIOutput)
364         {
365             //nothing to decode
366         }
367         else
368         {
369             throw new IllegalArgumentException("Unsupported component class " + component.getClass().getName());
370         }
371     }
372 
373 
374     public Object getConvertedValue(FacesContext facesContext, UIComponent component, Object submittedValue)
375         throws ConverterException
376     {
377         org.apache.myfaces.shared.renderkit.RendererUtils.checkParamValidity(facesContext, component, UIOutput.class);
378         return RendererUtils.getConvertedUIOutputValue(facesContext,
379                                                        (UIOutput)component,
380                                                        submittedValue);
381     }
382 
383     /**
384      * Returns the HTML type attribute of HTML input element, which is being rendered.
385      */
386     protected String getInputHtmlType(UIComponent component)
387     {
388         //subclasses may act on properties of the component
389         return HTML.INPUT_TYPE_TEXT;
390     }
391 
392     public static void renderOutputText(FacesContext facesContext,
393             UIComponent component, String text, boolean escape)
394             throws IOException
395     {
396         if (text != null)
397         {
398             ResponseWriter writer = facesContext.getResponseWriter();
399             boolean span = false;
400 
401             if (component.getId() != null
402                     && !component.getId().startsWith(
403                             UIViewRoot.UNIQUE_ID_PREFIX))
404             {
405                 span = true;
406 
407                 writer.startElement(HTML.SPAN_ELEM, component);
408 
409                 HtmlRendererUtils.writeIdIfNecessary(writer, component,
410                         facesContext);
411 
412                 HtmlRendererUtils.renderHTMLAttributes(writer, component,
413                         HTML.COMMON_PASSTROUGH_ATTRIBUTES);
414 
415             }
416             else
417             {
418                 span = HtmlRendererUtils
419                         .renderHTMLAttributesWithOptionalStartElement(writer,
420                                 component, HTML.SPAN_ELEM,
421                                 HTML.COMMON_PASSTROUGH_ATTRIBUTES);
422             }
423 
424             if (escape)
425             {
426                 if (log.isLoggable(Level.FINE))
427                 {
428                     log.fine("renderOutputText writing '" + text + "'");
429                 }
430                 writer.writeText(text,
431                         org.apache.myfaces.shared.renderkit.JSFAttr.VALUE_ATTR);
432             }
433             else
434             {
435                 writer.write(text);
436             }
437 
438             if (span)
439             {
440                 writer.endElement(org.apache.myfaces.shared.renderkit.html.HTML.SPAN_ELEM);
441             }
442         }
443     }
444 }