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  
20  package org.apache.myfaces.shared.renderkit.html;
21  
22  import javax.faces.component.UIComponent;
23  import javax.faces.component.UIInput;
24  import javax.faces.context.FacesContext;
25  import javax.faces.context.ResponseWriter;
26  import javax.faces.convert.ConverterException;
27  import javax.servlet.ServletException;
28  import javax.servlet.http.HttpServletRequest;
29  import javax.servlet.http.Part;
30  import java.io.IOException;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.logging.Level;
34  import java.util.logging.Logger;
35  import javax.faces.FacesException;
36  import javax.faces.application.FacesMessage;
37  import javax.faces.application.ProjectStage;
38  import javax.faces.component.UIViewRoot;
39  import javax.faces.component.behavior.ClientBehavior;
40  import javax.faces.component.behavior.ClientBehaviorHolder;
41  import javax.faces.component.html.HtmlForm;
42  import javax.faces.component.html.HtmlInputText;
43  import org.apache.myfaces.shared.renderkit.html.util.FormInfo;
44  import org.apache.myfaces.shared.renderkit.html.util.HttpPartWrapper;
45  import org.apache.myfaces.shared.util._ComponentUtils;
46  
47  public class HtmlInputFileRendererBase extends HtmlRenderer
48  {
49      private static final Logger log = Logger.getLogger(HtmlInputFileRendererBase.class.getName());
50      
51      private static final String AUTOCOMPLETE_VALUE_OFF = "off";
52  
53      @Override
54      public void decode(FacesContext facesContext, UIComponent component)
55      {
56          try
57          {
58             Part part = ((HttpServletRequest) facesContext.getExternalContext().getRequest()).
59                     getPart(component.getClientId());
60  
61             if (part == null)
62             {
63                 return;
64             }
65             ((UIInput) component).setSubmittedValue(new HttpPartWrapper(part));
66          }
67          catch (IOException e)
68          {
69              throw new FacesException(e);
70          }
71          catch (ServletException e)
72          {
73              throw new FacesException(e);
74          }
75          
76          if (component instanceof ClientBehaviorHolder &&
77                  !HtmlRendererUtils.isDisabled(component))
78          {
79              HtmlRendererUtils.decodeClientBehaviors(facesContext, component);
80          }
81      }
82      
83      @Override
84      public void encodeEnd(FacesContext facesContext, UIComponent component)
85          throws IOException
86      {   
87          renderInput(facesContext, component);
88          
89          if(!facesContext.isProjectStage(ProjectStage.Production) 
90                && facesContext.isPostback() &&
91               (facesContext.getPartialViewContext().isPartialRequest() ||
92                facesContext.getPartialViewContext().isAjaxRequest()))
93          {
94              FormInfo formInfo = _ComponentUtils.findNestingForm(component, facesContext);
95              if (formInfo != null && formInfo.getForm() instanceof HtmlForm)
96              {
97                  HtmlForm form = (HtmlForm) formInfo.getForm();
98                  String content = form.getEnctype();
99                  if (content==null || !content.contains("multipart/form-data"))
100                 {
101                      FacesMessage message = new FacesMessage("file upload requires a form with"+
102                             " enctype equal to multipart/form-data");
103                      facesContext.addMessage(component.getClientId(), message);
104                 }
105             }
106         }
107     }
108    
109     @Override
110     public Object getConvertedValue(FacesContext context, UIComponent component, Object submittedValue)
111             throws ConverterException
112     {
113         if (context == null)
114         {
115             throw new NullPointerException("context");
116         }
117         if (component == null)
118         {
119             throw new NullPointerException("component");
120         }
121         return submittedValue;
122     }
123 
124     /**
125      * Returns the HTML type attribute of HTML input element, which is being rendered.
126      */
127     protected String getInputHtmlType(UIComponent component)
128     {
129         //subclasses may act on properties of the component
130         return HTML.INPUT_TYPE_FILE;
131     }
132 
133     protected void renderValue(FacesContext facesContext, UIComponent component, ResponseWriter writer)
134             throws IOException
135     {
136         //the input file element cannot render a value it is readonly
137     }
138     
139     protected boolean isRenderOutputEventAttributes()
140     {
141         return true;
142     }
143 
144     protected void renderInput(FacesContext facesContext, UIComponent component)
145         throws IOException
146     {
147         //allow subclasses to render custom attributes by separating rendering begin and end 
148         renderInputBegin(facesContext, component);
149         renderInputEnd(facesContext, component);
150     }
151 
152     //Subclasses can set the value of an attribute before, or can render a custom attribute after calling this method
153     protected void renderInputBegin(FacesContext facesContext,
154             UIComponent component) throws IOException
155     {
156         ResponseWriter writer = facesContext.getResponseWriter();
157 
158         String clientId = component.getClientId(facesContext);
159 
160         writer.startElement(HTML.INPUT_ELEM, component);
161         writer.writeAttribute(HTML.ID_ATTR, clientId, null);
162         writer.writeAttribute(HTML.NAME_ATTR, clientId, null);
163         
164         //allow extending classes to modify html input element's type
165         String inputHtmlType = getInputHtmlType(component);
166         writer.writeAttribute(HTML.TYPE_ATTR, inputHtmlType, null);
167 
168         renderValue(facesContext, component, writer);
169 
170         Map<String, List<ClientBehavior>> behaviors = null;
171         if (component instanceof ClientBehaviorHolder)
172         {
173             behaviors = ((ClientBehaviorHolder) component).getClientBehaviors();
174             
175             long commonPropertiesMarked = 0L;
176             if (isCommonPropertiesOptimizationEnabled(facesContext))
177             {
178                 commonPropertiesMarked = CommonPropertyUtils.getCommonPropertiesMarked(component);
179             }
180             if (behaviors.isEmpty() && isCommonPropertiesOptimizationEnabled(facesContext))
181             {
182                 CommonPropertyUtils.renderChangeEventProperty(writer, 
183                         commonPropertiesMarked, component);
184                 CommonPropertyUtils.renderEventProperties(writer, 
185                         commonPropertiesMarked, component);
186                 CommonPropertyUtils.renderFieldEventPropertiesWithoutOnchange(writer, 
187                         commonPropertiesMarked, component);
188             }
189             else
190             {
191                 HtmlRendererUtils.renderBehaviorizedOnchangeEventHandler(facesContext, writer, component, behaviors);
192                 if (isCommonEventsOptimizationEnabled(facesContext))
193                 {
194                     Long commonEventsMarked = CommonEventUtils.getCommonEventsMarked(component);
195                     CommonEventUtils.renderBehaviorizedEventHandlers(facesContext, writer, 
196                             commonPropertiesMarked, commonEventsMarked, component, behaviors);
197                     CommonEventUtils.renderBehaviorizedFieldEventHandlersWithoutOnchange(
198                         facesContext, writer, commonPropertiesMarked, commonEventsMarked, component, behaviors);
199                 }
200                 else
201                 {
202                     HtmlRendererUtils.renderBehaviorizedEventHandlers(facesContext, writer, component, behaviors);
203                     HtmlRendererUtils.renderBehaviorizedFieldEventHandlersWithoutOnchange(
204                             facesContext, writer, component, behaviors);
205                 }
206             }
207             if (isCommonPropertiesOptimizationEnabled(facesContext))
208             {
209                 CommonPropertyUtils.renderInputPassthroughPropertiesWithoutDisabledAndEvents(writer, 
210                         commonPropertiesMarked, component);
211             }
212             else
213             {
214                 HtmlRendererUtils.renderHTMLAttributes(writer, component, 
215                         HTML.INPUT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED_AND_EVENTS);
216             }
217         }
218         else
219         {
220             if (isCommonPropertiesOptimizationEnabled(facesContext))
221             {
222                 CommonPropertyUtils.renderInputPassthroughPropertiesWithoutDisabled(writer, 
223                         CommonPropertyUtils.getCommonPropertiesMarked(component), component);
224             }
225             else
226             {
227                 HtmlRendererUtils.renderHTMLAttributes(writer, component, 
228                         HTML.INPUT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED);
229             }
230         }
231 
232         if (isDisabled(facesContext, component))
233         {
234             writer.writeAttribute(HTML.DISABLED_ATTR, Boolean.TRUE, null);
235         }
236 
237         if (isAutocompleteOff(facesContext, component))
238         {
239             writer.writeAttribute(HTML.AUTOCOMPLETE_ATTR, AUTOCOMPLETE_VALUE_OFF, HTML.AUTOCOMPLETE_ATTR);
240         }
241     }
242 
243     protected void renderInputEnd(FacesContext facesContext, UIComponent component) throws IOException
244     {
245         ResponseWriter writer = facesContext.getResponseWriter(); 
246 
247         writer.endElement(HTML.INPUT_ELEM);
248     }
249 
250     protected boolean isDisabled(FacesContext facesContext, UIComponent component)
251     {
252         //TODO: overwrite in extended HtmlTextRenderer and check for enabledOnUserRole
253         if (component instanceof HtmlInputText)
254         {
255             return ((HtmlInputText)component).isDisabled();
256         }
257 
258         return org.apache.myfaces.shared.renderkit.RendererUtils.getBooleanAttribute(component, 
259                 HTML.DISABLED_ATTR, false);
260         
261     }
262 
263     /**
264      * If autocomplete is "on" or not set, do not render it
265      */
266     protected boolean isAutocompleteOff(FacesContext facesContext, UIComponent component)
267     {
268         if (component instanceof HtmlInputText)
269         {
270             String autocomplete = ((HtmlInputText)component).getAutocomplete();
271             if (autocomplete != null)
272             {
273                 return autocomplete.equals(AUTOCOMPLETE_VALUE_OFF);
274             }
275         }
276 
277         return false;
278     }
279 
280     public static void renderOutputText(FacesContext facesContext,
281             UIComponent component, String text, boolean escape)
282             throws IOException
283     {
284         if (text != null)
285         {
286             ResponseWriter writer = facesContext.getResponseWriter();
287             boolean span = false;
288 
289             if (component.getId() != null
290                     && !component.getId().startsWith(
291                             UIViewRoot.UNIQUE_ID_PREFIX))
292             {
293                 span = true;
294 
295                 writer.startElement(HTML.SPAN_ELEM, component);
296 
297                 HtmlRendererUtils.writeIdIfNecessary(writer, component,
298                         facesContext);
299 
300                 HtmlRendererUtils.renderHTMLAttributes(writer, component,
301                         HTML.COMMON_PASSTROUGH_ATTRIBUTES);
302 
303             }
304             else
305             {
306                 span = HtmlRendererUtils
307                         .renderHTMLAttributesWithOptionalStartElement(writer,
308                                 component, HTML.SPAN_ELEM,
309                                 HTML.COMMON_PASSTROUGH_ATTRIBUTES);
310             }
311 
312             if (escape)
313             {
314                 if (log.isLoggable(Level.FINE))
315                 {
316                     log.fine("renderOutputText writing '" + text + "'");
317                 }
318                 writer.writeText(text,
319                         org.apache.myfaces.shared.renderkit.JSFAttr.VALUE_ATTR);
320             }
321             else
322             {
323                 writer.write(text);
324             }
325 
326             if (span)
327             {
328                 writer.endElement(org.apache.myfaces.shared.renderkit.html.HTML.SPAN_ELEM);
329             }
330         }
331     }
332 }