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.JavascriptUtils;
43  import org.apache.myfaces.shared.renderkit.html.util.ResourceUtils;
44  
45  /**
46   * @author Thomas Spiegl (latest modification by $Author: lu4242 $)
47   * @author Manfred Geiler
48   * @version $Revision: 1230370 $ $Date: 2012-01-11 21:07:58 -0500 (Wed, 11 Jan 2012) $
49   */
50  public class HtmlTextRendererBase
51          extends HtmlRenderer
52  {
53      //private static final Log log = LogFactory.getLog(HtmlTextRendererBase.class);
54      private static final Logger log = Logger.getLogger(HtmlTextRendererBase.class.getName());
55  
56      private static final String AUTOCOMPLETE_VALUE_OFF = "off";
57  
58      public void encodeEnd(FacesContext facesContext, UIComponent component)
59          throws IOException
60      {
61          org.apache.myfaces.shared.renderkit.RendererUtils.checkParamValidity(facesContext,component,null);
62          
63          Map<String, List<ClientBehavior>> behaviors = null;
64          if (component instanceof ClientBehaviorHolder)
65          {
66              behaviors = ((ClientBehaviorHolder) component).getClientBehaviors();
67              if (!behaviors.isEmpty())
68              {
69                  ResourceUtils.renderDefaultJsfJsInlineIfNecessary(facesContext, facesContext.getResponseWriter());
70              }
71          }
72          
73          if (component instanceof UIInput)
74          {
75              renderInput(facesContext, component);
76          }
77          else if (component instanceof UIOutput)
78          {
79              renderOutput(facesContext, component);
80          }
81          else
82          {
83              throw new IllegalArgumentException("Unsupported component class " + component.getClass().getName());
84          }
85      }
86  
87  
88      protected void renderOutput(FacesContext facesContext, UIComponent component)
89          throws IOException
90      {
91          
92          String text = org.apache.myfaces.shared.renderkit.RendererUtils.getStringValue(facesContext, component);
93          if (log.isLoggable(Level.FINE))
94          {
95              log.fine("renderOutput '" + text + "'");
96          }
97          boolean escape;
98          if (component instanceof HtmlOutputText || component instanceof EscapeCapable)
99          {
100             escape = ((HtmlOutputText)component).isEscape();
101         }
102         else
103         {
104             escape = RendererUtils.getBooleanAttribute(component, 
105                     org.apache.myfaces.shared.renderkit.JSFAttr.ESCAPE_ATTR,
106                                                        true); //default is to escape
107         }
108         if (text != null)
109         {
110             ResponseWriter writer = facesContext.getResponseWriter();
111             boolean span = false;
112 
113             if (isCommonPropertiesOptimizationEnabled(facesContext))
114             {
115                 long commonPropertiesMarked = CommonPropertyUtils.getCommonPropertiesMarked(component);
116                 if ( (commonPropertiesMarked & ~(CommonPropertyConstants.ESCAPE_PROP)) > 0)
117                 {
118                     span = true;
119                     writer.startElement(HTML.SPAN_ELEM, component);
120                     HtmlRendererUtils.writeIdIfNecessary(writer, component, facesContext);
121                 }
122                 else if (CommonPropertyUtils.isIdRenderingNecessary(component))
123                 {
124                     span = true;
125                     writer.startElement(HTML.SPAN_ELEM, component);
126                     writer.writeAttribute(HTML.ID_ATTR, component.getClientId(facesContext), null);
127                 }
128                 
129                 CommonPropertyUtils.renderUniversalProperties(writer, commonPropertiesMarked, component);
130                 CommonPropertyUtils.renderStyleProperties(writer, commonPropertiesMarked, component);
131                 
132                 if (isRenderOutputEventAttributes())
133                 {
134                     HtmlRendererUtils.renderHTMLAttributes(writer, component, HTML.EVENT_HANDLER_ATTRIBUTES);
135                 }
136             }
137             else
138             {
139                 if(component.getId()!=null && !component.getId().startsWith(UIViewRoot.UNIQUE_ID_PREFIX))
140                 {
141                     span = true;
142     
143                     writer.startElement(HTML.SPAN_ELEM, component);
144     
145                     HtmlRendererUtils.writeIdIfNecessary(writer, component, facesContext);
146     
147                     HtmlRendererUtils.renderHTMLAttributes(writer, component, HTML.COMMON_PASSTROUGH_ATTRIBUTES);
148     
149                 }
150                 else
151                 {
152                     span = HtmlRendererUtils.renderHTMLAttributesWithOptionalStartElement(writer,component,
153                             HTML.SPAN_ELEM,HTML.COMMON_PASSTROUGH_ATTRIBUTES);
154                 }
155             }
156 
157             if (escape)
158             {
159                 if (log.isLoggable(Level.FINE))
160                 {
161                     log.fine("renderOutputText writing '" + text + "'");
162                 }
163                 writer.writeText(text, org.apache.myfaces.shared.renderkit.JSFAttr.VALUE_ATTR);
164             }
165             else
166             {
167                 writer.write(text);
168             }
169 
170             if(span)
171             {
172                 writer.endElement(org.apache.myfaces.shared.renderkit.html.HTML.SPAN_ELEM);
173             }
174         }
175     }
176 
177     protected boolean isRenderOutputEventAttributes()
178     {
179         return true;
180     }
181 
182     protected void renderInput(FacesContext facesContext, UIComponent component)
183         throws IOException
184     {
185         //allow subclasses to render custom attributes by separating rendering begin and end 
186         renderInputBegin(facesContext, component);
187         renderInputEnd(facesContext, component);
188     }
189 
190     //Subclasses can set the value of an attribute before, or can render a custom attribute after calling this method
191     protected void renderInputBegin(FacesContext facesContext,
192             UIComponent component) throws IOException
193     {
194         ResponseWriter writer = facesContext.getResponseWriter();
195 
196         String clientId = component.getClientId(facesContext);
197         String value = org.apache.myfaces.shared.renderkit.RendererUtils.getStringValue(facesContext, component);
198         if (log.isLoggable(Level.FINE))
199         {
200             log.fine("renderInput '" + value + "'");
201         }
202         writer.startElement(HTML.INPUT_ELEM, component);
203         writer.writeAttribute(HTML.ID_ATTR, clientId, null);
204         writer.writeAttribute(HTML.NAME_ATTR, clientId, null);
205         
206         //allow extending classes to modify html input element's type
207         String inputHtmlType = getInputHtmlType(component);
208         writer.writeAttribute(HTML.TYPE_ATTR, inputHtmlType, null);
209         
210         if (value != null)
211         {
212             writer.writeAttribute(HTML.VALUE_ATTR, value, JSFAttr.VALUE_ATTR);
213         }
214 
215         Map<String, List<ClientBehavior>> behaviors = null;
216         if (component instanceof ClientBehaviorHolder && JavascriptUtils.isJavascriptAllowed(
217                 facesContext.getExternalContext()))
218         {
219             behaviors = ((ClientBehaviorHolder) component).getClientBehaviors();
220             
221             long commonPropertiesMarked = 0L;
222             if (isCommonPropertiesOptimizationEnabled(facesContext))
223             {
224                 commonPropertiesMarked = CommonPropertyUtils.getCommonPropertiesMarked(component);
225             }
226             if (behaviors.isEmpty() && isCommonPropertiesOptimizationEnabled(facesContext))
227             {
228                 CommonPropertyUtils.renderChangeEventProperty(writer, 
229                         commonPropertiesMarked, component);
230                 CommonPropertyUtils.renderEventProperties(writer, 
231                         commonPropertiesMarked, component);
232                 CommonPropertyUtils.renderFieldEventPropertiesWithoutOnchange(writer, 
233                         commonPropertiesMarked, component);
234             }
235             else
236             {
237                 HtmlRendererUtils.renderBehaviorizedOnchangeEventHandler(facesContext, writer, component, behaviors);
238                 if (isCommonEventsOptimizationEnabled(facesContext))
239                 {
240                     Long commonEventsMarked = CommonEventUtils.getCommonEventsMarked(component);
241                     CommonEventUtils.renderBehaviorizedEventHandlers(facesContext, writer, 
242                             commonPropertiesMarked, commonEventsMarked, component, behaviors);
243                     CommonEventUtils.renderBehaviorizedFieldEventHandlersWithoutOnchange(
244                         facesContext, writer, commonPropertiesMarked, commonEventsMarked, component, behaviors);
245                 }
246                 else
247                 {
248                     HtmlRendererUtils.renderBehaviorizedEventHandlers(facesContext, writer, component, behaviors);
249                     HtmlRendererUtils.renderBehaviorizedFieldEventHandlersWithoutOnchange(
250                             facesContext, writer, component, behaviors);
251                 }
252             }
253             if (isCommonPropertiesOptimizationEnabled(facesContext))
254             {
255                 CommonPropertyUtils.renderInputPassthroughPropertiesWithoutDisabledAndEvents(writer, 
256                         commonPropertiesMarked, component);
257             }
258             else
259             {
260                 HtmlRendererUtils.renderHTMLAttributes(writer, component, 
261                         HTML.INPUT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED_AND_EVENTS);
262             }
263         }
264         else
265         {
266             if (isCommonPropertiesOptimizationEnabled(facesContext))
267             {
268                 CommonPropertyUtils.renderInputPassthroughPropertiesWithoutDisabled(writer, 
269                         CommonPropertyUtils.getCommonPropertiesMarked(component), component);
270             }
271             else
272             {
273                 HtmlRendererUtils.renderHTMLAttributes(writer, component, 
274                         HTML.INPUT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED);
275             }
276         }
277 
278         if (isDisabled(facesContext, component))
279         {
280             writer.writeAttribute(HTML.DISABLED_ATTR, Boolean.TRUE, null);
281         }
282 
283         if (isAutocompleteOff(facesContext, component))
284         {
285             writer.writeAttribute(HTML.AUTOCOMPLETE_ATTR, AUTOCOMPLETE_VALUE_OFF, HTML.AUTOCOMPLETE_ATTR);
286         }
287     }
288     
289     protected void renderInputEnd(FacesContext facesContext, UIComponent component) throws IOException
290     {
291         ResponseWriter writer = facesContext.getResponseWriter(); 
292 
293         writer.endElement(HTML.INPUT_ELEM);
294     }
295 
296     protected boolean isDisabled(FacesContext facesContext, UIComponent component)
297     {
298         //TODO: overwrite in extended HtmlTextRenderer and check for enabledOnUserRole
299         if (component instanceof HtmlInputText)
300         {
301             return ((HtmlInputText)component).isDisabled();
302         }
303 
304         return org.apache.myfaces.shared.renderkit.RendererUtils.getBooleanAttribute(component, 
305                 HTML.DISABLED_ATTR, false);
306         
307     }
308 
309     /**
310      * If autocomplete is "on" or not set, do not render it
311      */
312     protected boolean isAutocompleteOff(FacesContext facesContext, UIComponent component)
313     {
314         if (component instanceof HtmlInputText)
315         {
316             String autocomplete = ((HtmlInputText)component).getAutocomplete();
317             if (autocomplete != null)
318             {
319                 return autocomplete.equals(AUTOCOMPLETE_VALUE_OFF);
320             }
321         }
322 
323         return false;
324     }
325 
326 
327     public void decode(FacesContext facesContext, UIComponent component)
328     {
329         RendererUtils.checkParamValidity(facesContext,component,null);
330 
331         if (component instanceof UIInput)
332         {
333             HtmlRendererUtils.decodeUIInput(facesContext, component);
334             if (component instanceof ClientBehaviorHolder &&
335                     !HtmlRendererUtils.isDisabled(component))
336             {
337                 HtmlRendererUtils.decodeClientBehaviors(facesContext, component);
338             }
339         }
340         else if (component instanceof UIOutput)
341         {
342             //nothing to decode
343         }
344         else
345         {
346             throw new IllegalArgumentException("Unsupported component class " + component.getClass().getName());
347         }
348     }
349 
350 
351     public Object getConvertedValue(FacesContext facesContext, UIComponent component, Object submittedValue)
352         throws ConverterException
353     {
354         org.apache.myfaces.shared.renderkit.RendererUtils.checkParamValidity(facesContext, component, UIOutput.class);
355         return RendererUtils.getConvertedUIOutputValue(facesContext,
356                                                        (UIOutput)component,
357                                                        submittedValue);
358     }
359 
360     /**
361      * Returns the HTML type attribute of HTML input element, which is being rendered.
362      */
363     protected String getInputHtmlType(UIComponent component)
364     {
365         //subclasses may act on properties of the component
366         return HTML.INPUT_TYPE_TEXT;
367     }
368 
369     public static void renderOutputText(FacesContext facesContext,
370             UIComponent component, String text, boolean escape)
371             throws IOException
372     {
373         if (text != null)
374         {
375             ResponseWriter writer = facesContext.getResponseWriter();
376             boolean span = false;
377 
378             if (component.getId() != null
379                     && !component.getId().startsWith(
380                             UIViewRoot.UNIQUE_ID_PREFIX))
381             {
382                 span = true;
383 
384                 writer.startElement(HTML.SPAN_ELEM, component);
385 
386                 HtmlRendererUtils.writeIdIfNecessary(writer, component,
387                         facesContext);
388 
389                 HtmlRendererUtils.renderHTMLAttributes(writer, component,
390                         HTML.COMMON_PASSTROUGH_ATTRIBUTES);
391 
392             }
393             else
394             {
395                 span = HtmlRendererUtils
396                         .renderHTMLAttributesWithOptionalStartElement(writer,
397                                 component, HTML.SPAN_ELEM,
398                                 HTML.COMMON_PASSTROUGH_ATTRIBUTES);
399             }
400 
401             if (escape)
402             {
403                 if (log.isLoggable(Level.FINE))
404                 {
405                     log.fine("renderOutputText writing '" + text + "'");
406                 }
407                 writer.writeText(text,
408                         org.apache.myfaces.shared.renderkit.JSFAttr.VALUE_ATTR);
409             }
410             else
411             {
412                 writer.write(text);
413             }
414 
415             if (span)
416             {
417                 writer.endElement(org.apache.myfaces.shared.renderkit.html.HTML.SPAN_ELEM);
418             }
419         }
420     }
421 }